home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 37 / IOPROG_37.ISO / SOFT / Multilizer.exe / disk1 / data1.cab / data1 / [Group9]VCL Source Standard / ivdbgrid.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-08-12  |  110.9 KB  |  4,152 lines

  1. unit IvDBGrid;
  2.  
  3. {$I IVMULTI.INC}
  4.  
  5. interface
  6.  
  7. {$IFDEF IVBIDI}
  8. uses
  9.   DBGrids;
  10.  
  11. type
  12.   TIvDBGrid = class(TDBGrid)
  13.   end;
  14. {$ELSE}
  15.  
  16. {$R-}
  17.  
  18. uses
  19.   Windows, SysUtils, Messages, Classes, Controls, Forms, StdCtrls,
  20.   Graphics, IvGrids, DBCtrls, Db, Menus;
  21.  
  22. type
  23.   TIvColumnValue = (cvColor, cvWidth, cvFont, cvAlignment, cvReadOnly,
  24.     cvTitleColor, cvTitleCaption, cvTitleAlignment, cvTitleFont
  25. {$IFDEF IVIME}
  26.     , cvImeMode, cvImeName
  27. {$ENDIF}
  28.     );
  29.   TIvColumnValues = set of TIvColumnValue;
  30.  
  31. const
  32.   ColumnTitleValues = [cvTitleColor..cvTitleFont];
  33.   cm_DeferLayout = WM_USER + 100;
  34.  
  35. type
  36.   TIvColumn = class;
  37.   TIvCustomDBGrid = class;
  38.  
  39.   TIvColumnTitle = class(TPersistent)
  40.   private
  41.     FColumn: TIvColumn;
  42.     FCaption: String;
  43.     FFont: TFont;
  44.     FColor: TColor;
  45.     FAlignment: TAlignment;
  46.  
  47.     procedure FontChanged(Sender: TObject);
  48.     function GetAlignment: TAlignment;
  49.     function GetColor: TColor;
  50.     function GetCaption: String;
  51.     function GetFont: TFont;
  52.     function IsAlignmentStored: Boolean;
  53.     function IsColorStored: Boolean;
  54.     function IsFontStored: Boolean;
  55.     function IsCaptionStored: Boolean;
  56.     procedure SetAlignment(Value: TAlignment);
  57.     procedure SetColor(Value: TColor);
  58.     procedure SetFont(Value: TFont);
  59.     procedure SetCaption(const Value: String); virtual;
  60.  
  61.   protected
  62.     procedure RefreshDefaultFont;
  63.  
  64.   public
  65.     constructor Create(Column: TIvColumn);
  66.     destructor Destroy; override;
  67.     procedure Assign(Source: TPersistent); override;
  68.     function DefaultAlignment: TAlignment;
  69.     function DefaultColor: TColor;
  70.     function DefaultFont: TFont;
  71.     function DefaultCaption: String;
  72.     procedure RestoreDefaults; virtual;
  73.  
  74.   published
  75.     property Alignment: TAlignment read GetAlignment write SetAlignment stored IsAlignmentStored;
  76.     property Caption: String read GetCaption write SetCaption stored IsCaptionStored;
  77.     property Color: TColor read GetColor write SetColor stored IsColorStored;
  78.     property Font: TFont read GetFont write SetFont stored IsFontStored;
  79.   end;
  80.  
  81.   TIvColumnButtonStyle = (cbsAuto, cbsEllipsis, cbsNone);
  82.  
  83.   TIvColumn = class(TCollectionItem)
  84.   private
  85.     FField: TField;
  86.     FFieldName: String;
  87.     FColor: TColor;
  88.     FWidth: Integer;
  89.     FTitle: TIvColumnTitle;
  90.     FFont: TFont;
  91.     FPickList: TStrings;
  92.     FPopupMenu: TPopupMenu;
  93.     FDropDownRows: Cardinal;
  94.     FButtonStyle: TIvColumnButtonStyle;
  95.     FAlignment: TAlignment;
  96.     FReadonly: Boolean;
  97.     FAssignedValues: TIvColumnValues;
  98. {$IFDEF IVIME}
  99.     FImeMode: TImeMode;
  100.     FImeName: TImeName;
  101.  
  102.     function  GetImeMode: TImeMode;
  103.     function  GetImeName: TImeName;
  104.     procedure SetImeMode(Value: TImeMode); virtual;
  105.     procedure SetImeName(Value: TImeName); virtual;
  106. {$ENDIF}
  107.  
  108.     procedure FontChanged(Sender: TObject);
  109.     function  GetAlignment: TAlignment;
  110.     function  GetColor: TColor;
  111.     function  GetField: TField;
  112.     function  GetFont: TFont;
  113.     function  GetPickList: TStrings;
  114.     function  GetReadOnly: Boolean;
  115.     function  GetWidth: Integer;
  116.     function  IsAlignmentStored: Boolean;
  117.     function  IsColorStored: Boolean;
  118.     function  IsFontStored: Boolean;
  119. {$IFDEF IVIME}
  120.     function  IsImeModeStored: Boolean;
  121.     function  IsImeNameStored: Boolean;
  122. {$ENDIF}
  123.     function  IsReadOnlyStored: Boolean;
  124.     function  IsWidthStored: Boolean;
  125.     procedure SetAlignment(Value: TAlignment); virtual;
  126.     procedure SetButtonStyle(Value: TIvColumnButtonStyle);
  127.     procedure SetColor(Value: TColor);
  128.     procedure SetField(Value: TField); virtual;
  129.     procedure SetFieldName(const Value: String);
  130.     procedure SetFont(Value: TFont);
  131.     procedure SetPickList(Value: TStrings);
  132.     procedure SetPopupMenu(Value: TPopupMenu);
  133.     procedure SetReadOnly(Value: Boolean); virtual;
  134.     procedure SetTitle(Value: TIvColumnTitle);
  135.     procedure SetWidth(Value: Integer); virtual;
  136.  
  137.   protected
  138.     function  CreateTitle: TIvColumnTitle; virtual;
  139.     function  GetGrid: TIvCustomDBGrid;
  140. {$IFDEF IVWIDE}
  141.     function GetDisplayName: String; override;
  142. {$ENDIF}
  143.     procedure RefreshDefaultFont;
  144.  
  145.   public
  146.     constructor Create(Collection: TCollection); override;
  147.     destructor Destroy; override;
  148.  
  149.     procedure Assign(Source: TPersistent); override;
  150.     function  DefaultAlignment: TAlignment;
  151.     function  DefaultColor: TColor;
  152.     function  DefaultFont: TFont;
  153. {$IFDEF IVIME}
  154.     function  DefaultImeMode: TImeMode;
  155.     function  DefaultImeName: TImeName;
  156. {$ENDIF}
  157.     function  DefaultReadOnly: Boolean;
  158.     function  DefaultWidth: Integer;
  159.     procedure RestoreDefaults; virtual;
  160.  
  161.     property  Grid: TIvCustomDBGrid read GetGrid;
  162.     property  AssignedValues: TIvColumnValues read FAssignedValues;
  163.     property  Field: TField read GetField write SetField;
  164.  
  165.   published
  166.     property  Alignment: TAlignment read GetAlignment write SetAlignment stored IsAlignmentStored;
  167.     property  ButtonStyle: TIvColumnButtonStyle read FButtonStyle write SetButtonStyle default cbsAuto;
  168.     property  Color: TColor read GetColor write SetColor stored IsColorStored;
  169.     property  DropDownRows: Cardinal read FDropDownRows write FDropDownRows default 7;
  170.     property  FieldName: String read FFieldName write SetFieldName;
  171.     property  Font: TFont read GetFont write SetFont stored IsFontStored;
  172.     property  PickList: TStrings read GetPickList write SetPickList;
  173.     property  PopupMenu: TPopupMenu read FPopupMenu write SetPopupMenu;
  174.     property  ReadOnly: Boolean read GetReadOnly write SetReadOnly stored IsReadOnlyStored;
  175.     property  Title: TIvColumnTitle read FTitle write SetTitle;
  176.     property  Width: Integer read GetWidth write SetWidth stored IsWidthStored;
  177. {$IFDEF IVIME}
  178.     property  ImeMode: TImeMode read GetImeMode write SetImeMode stored IsImeModeStored;
  179.     property  ImeName: TImeName read GetImeName write SetImeName stored IsImeNameStored;
  180. {$ENDIF}
  181.   end;
  182.  
  183.   TIvColumnClass = class of TIvColumn;
  184.  
  185.   TIvDBGridColumnsState = (csDefault, csCustomized);
  186.  
  187.   TIvDBGridColumns = class(TCollection)
  188.   private
  189.     FGrid: TIvCustomDBGrid;
  190.  
  191.     function GeTIvColumn(Index: Integer): TIvColumn;
  192.     function GetState: TIvDBGridColumnsState;
  193.     procedure SeTIvColumn(Index: Integer; Value: TIvColumn);
  194.     procedure SetState(NewState: TIvDBGridColumnsState);
  195.  
  196.   protected
  197. {$IFDEF IVWIDE}
  198.     function GetOwner: TPersistent; override;
  199. {$ENDIF}
  200.     procedure Update(Item: TCollectionItem); override;
  201.  
  202.   public
  203.     constructor Create(Grid: TIvCustomDBGrid; ColumnClass: TIvColumnClass);
  204.  
  205.     function  Add: TIvColumn;
  206.     procedure LoadFromFile(const Filename: string);
  207.     procedure LoadFromStream(S: TStream);
  208.     procedure RestoreDefaults;
  209.     procedure RebuildColumns;
  210.     procedure SaveToFile(const Filename: string);
  211.     procedure SaveToStream(S: TStream);
  212.  
  213.     property State: TIvDBGridColumnsState read GetState write SetState;
  214.     property Grid: TIvCustomDBGrid read FGrid;
  215.     property Items[Index: Integer]: TIvColumn read GeTIvColumn write SeTIvColumn; default;
  216.   end;
  217.  
  218.   TIvGridDataLink = class(TDataLink)
  219.   private
  220.     FGrid: TIvCustomDBGrid;
  221.     FFieldCount: Integer;
  222.     FFieldMapSize: Integer;
  223.     FFieldMap: Pointer;
  224.     FModified: Boolean;
  225.     FInUpdateData: Boolean;
  226.     FSparseMap: Boolean;
  227.  
  228.     function GetDefaultFields: Boolean;
  229.     function GetFields(I: Integer): TField;
  230.  
  231.   protected
  232.     procedure ActiveChanged; override;
  233.     procedure DataSetChanged; override;
  234.     procedure DataSetScrolled(Distance: Integer); override;
  235.     procedure FocusControl(Field: TFieldRef); override;
  236.     procedure EditingChanged; override;
  237.     procedure LayoutChanged; override;
  238.     procedure RecordChanged(Field: TField); override;
  239.     procedure UpdateData; override;
  240.     function  GetMappedIndex(ColIndex: Integer): Integer;
  241.  
  242.   public
  243.     constructor Create(AGrid: TIvCustomDBGrid);
  244.     destructor Destroy; override;
  245.  
  246.     function AddMapping(const FieldName: string): Boolean;
  247.     procedure ClearMapping;
  248.     procedure Modified;
  249.     procedure Reset;
  250.  
  251.     property DefaultFields: Boolean read GetDefaultFields;
  252.     property FieldCount: Integer read FFieldCount;
  253.     property Fields[I: Integer]: TField read GetFields;
  254.     property SparseMap: Boolean read FSparseMap write FSparseMap;
  255.   end;
  256.  
  257.   TIvBookmarkList = class
  258.   private
  259.     FList: TStringList;
  260.     FGrid: TIvCustomDBGrid;
  261.     FCache: TBookmarkStr;
  262.     FCacheIndex: Integer;
  263.     FCacheFind: Boolean;
  264.     FLinkActive: Boolean;
  265.     function GetCount: Integer;
  266.     function GetCurrentRowSelected: Boolean;
  267.     function GetItem(Index: Integer): TBookmarkStr;
  268.     procedure SetCurrentRowSelected(Value: Boolean);
  269.     procedure StringsChanged(Sender: TObject);
  270.   protected
  271.     function CurrentRow: TBookmarkStr;
  272.     function Compare(const Item1, Item2: TBookmarkStr): Integer;
  273.     procedure LinkActive(Value: Boolean);
  274.   public
  275.     constructor Create(AGrid: TIvCustomDBGrid);
  276.     destructor Destroy; override;
  277.     procedure Clear;           // free all bookmarks
  278.     procedure Delete;          // delete all selected rows from dataset
  279.     function  Find(const Item: TBookmarkStr; var Index: Integer): Boolean;
  280.     function  IndexOf(const Item: TBookmarkStr): Integer;
  281.     function  Refresh: Boolean;// drop orphaned bookmarks; True = orphans found
  282.     property Count: Integer read GetCount;
  283.     property CurrentRowSelected: Boolean read GetCurrentRowSelected
  284.       write SetCurrentRowSelected;
  285.     property Items[Index: Integer]: TBookmarkStr read GetItem; default;
  286.   end;
  287.  
  288.   TIvDBGridOption = (dgEditing, dgAlwaysShowEditor, dgTitles, dgIndicator,
  289.     dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect,
  290.     dgAlwaysShowSelection, dgConfirmDelete, dgCancelOnExit, dgMultiSelect);
  291.   TIvDBGridOptions = set of TIvDBGridOption;
  292.  
  293.   TIvDrawDataCellEvent = procedure (Sender: TObject; const Rect: TRect; Field: TField;
  294.     State: TIvGridDrawState) of object;
  295.  
  296.   TIvDrawColumnCellEvent = procedure (Sender: TObject; const Rect: TRect;
  297.     DataCol: Integer; Column: TIvColumn; State: TIvGridDrawState) of object;
  298.   TIvDBGridClickEvent = procedure (Column: TIvColumn) of object;
  299.  
  300.   TIvCustomDBGrid = class(TIvCustomGrid)
  301.   private
  302.     FIndicators: TImageList;
  303.     FTitleFont: TFont;
  304.     FReadOnly: Boolean;
  305.     FUserChange: Boolean;
  306.     FLayoutFromDataset: Boolean;
  307.     FOptions: TIvDBGridOptions;
  308.     FTitleOffset, FIndicatorOffset: Byte;
  309.     FUpdateLock: Byte;
  310.     FLayoutLock: Byte;
  311.     FInColExit: Boolean;
  312.     FDefaultDrawing: Boolean;
  313.     FSelfChangingTitleFont: Boolean;
  314.     FSelecting: Boolean;
  315.     FSelRow: Integer;
  316.     FDataLink: TIvGridDataLink;
  317.     FOnColEnter: TNotifyEvent;
  318.     FOnColExit: TNotifyEvent;
  319.     FOnDrawDataCell: TIvDrawDataCellEvent;
  320.     FOnDrawColumnCell: TIvDrawColumnCellEvent;
  321.     FEditText: String;
  322.     FColumns: TIvDBGridColumns;
  323.     FOnEditButtonClick: TNotifyEvent;
  324.     FOnColumnMoved: TIvMovedEvent;
  325.     FBookmarks: TIvBookmarkList;
  326.     FSelectionAnchor: TBookmarkStr;
  327. {$IFDEF IVIME}
  328.     FOriginalImeName: TImeName;
  329.     FOriginalImeMode: TImeMode;
  330. {$ENDIF}
  331.     FOnCellClick: TIvDBGridClickEvent;
  332.     FOnTitleClick:TIvDBGridClickEvent;
  333.  
  334.     function AcquireFocus: Boolean;
  335.     procedure DataChanged;
  336.     procedure EditingChanged;
  337.     function GetDataSource: TDataSource;
  338.     function GetFieldCount: Integer;
  339.     function GetFields(FieldIndex: Integer): TField;
  340.     function GetSelectedField: TField;
  341.     function GetSelectedIndex: Integer;
  342.     procedure InternalLayout;
  343.     procedure MoveCol(RawCol: Integer);
  344. {$IFDEF IVWIDE}
  345.     procedure ReadColumns(Reader: TReader);
  346.     procedure WriteColumns(Writer: TWriter);
  347. {$ENDIF}
  348.     procedure RecordChanged(Field: TField);
  349.     procedure SeTIvColumns(Value: TIvDBGridColumns);
  350.     procedure SetDataSource(Value: TDataSource);
  351.     procedure SetOptions(Value: TIvDBGridOptions);
  352.     procedure SetSelectedField(Value: TField);
  353.     procedure SetSelectedIndex(Value: Integer);
  354.     procedure SetTitleFont(Value: TFont);
  355.     procedure TitleFontChanged(Sender: TObject);
  356.     procedure UpdateData;
  357.     procedure UpdateActive;
  358. {$IFDEF IVIME}
  359.     procedure SetIme;
  360.     procedure UpdateIme;
  361. {$ENDIF}
  362.     procedure UpdateScrollBar;
  363.     procedure UpdateRowCount;
  364.     procedure CMExit(var Message: TMessage); message CM_EXIT;
  365.     procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  366.     procedure CMParentFontChanged(var Message: TMessage); message CM_PARENTFONTCHANGED;
  367.     procedure CMDeferLayout(var Message); message cm_DeferLayout;
  368.     procedure CMDesignHitTest(var Msg: TCMDesignHitTest); message CM_DESIGNHITTEST;
  369.     procedure WMSetCursor(var Msg: TWMSetCursor); message WM_SETCURSOR;
  370.     procedure WMSize(var Message: TWMSize); message WM_SIZE;
  371.     procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  372. {$IFDEF IVIME}
  373.     procedure WMIMEStartComp(var Message: TMessage); message WM_IME_STARTCOMPOSITION;
  374.     procedure WMSetFocus(var Message: TWMSetFocus); message WM_SetFOCUS;
  375.     procedure WMKillFocus(var Message: TMessage); message WM_KillFocus;
  376. {$ENDIF}
  377.  
  378.   protected
  379.     FUpdateFields: Boolean;
  380.     FAcquireFocus: Boolean;
  381.     FUpdatingEditor: Boolean;
  382.  
  383.     function  RawToDataColumn(ACol: Integer): Integer;
  384.     function  DataToRawColumn(ACol: Integer): Integer;
  385.     function  AcquireLayoutLock: Boolean;
  386.     procedure BeginLayout;
  387.     procedure BeginUpdate;
  388.     procedure CancelLayout;
  389.     function  CanEditAcceptKey(Key: Char): Boolean; override;
  390.     function  CanEditModify: Boolean; override;
  391.     function  CanEditShow: Boolean; override;
  392.     procedure CellClick(Column: TIvColumn); dynamic;
  393.     procedure ColumnMoved(FromIndex, ToIndex: Longint); override;
  394.     procedure ColEnter; dynamic;
  395.     procedure ColExit; dynamic;
  396.     procedure ColWidthsChanged; override;
  397.     function  CreateColumns: TIvDBGridColumns; dynamic;
  398.     function  CreateEditor: TIvInplaceEdit; override;
  399.     procedure CreateWnd; override;
  400.     procedure DeferLayout;
  401.     procedure DefaultHandler(var Msg); override;
  402.     procedure DefineFieldMap; virtual;
  403. {$IFDEF IVWIDE}
  404.     procedure DefineProperties(Filer: TFiler); override;
  405. {$ENDIF}
  406.     procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TIvGridDrawState); override;
  407.     procedure DrawDataCell(const Rect: TRect; Field: TField;
  408.       State: TIvGridDrawState); dynamic; { obsolete }
  409.     procedure DrawColumnCell(const Rect: TRect; DataCol: Integer;
  410.       Column: TIvColumn; State: TIvGridDrawState); dynamic;
  411.     procedure EditButtonClick; dynamic;
  412.     procedure EndLayout;
  413.     procedure EndUpdate;
  414.     function  GetColField(DataCol: Integer): TField;
  415.     function  GetEditLimit: Integer; override;
  416.     function  GetEditMask(ACol, ARow: Longint): string; override;
  417.     function  GetEditText(ACol, ARow: Longint): string; override;
  418.     function  GetFieldValue(ACol: Integer): string;
  419.     function  HighlightCell(DataCol, DataRow: Integer; const Value: string;
  420.       AState: TIvGridDrawState): Boolean; virtual;
  421.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  422.     procedure KeyPress(var Key: Char); override;
  423.     procedure LayoutChanged; virtual;
  424.     procedure LinkActive(Value: Boolean); virtual;
  425.     procedure Loaded; override;
  426.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  427.       X, Y: Integer); override;
  428.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
  429.       X, Y: Integer); override;
  430.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  431.     procedure Scroll(Distance: Integer); virtual;
  432.     procedure SeTIvColumnAttributes; virtual;
  433.     procedure SetEditText(ACol, ARow: Longint; const Value: string); override;
  434.     function  StoreColumns: Boolean;
  435.     procedure TimedScroll(Direction: TIvGridScrollDirection); override;
  436.     procedure TitleClick(Column: TIvColumn); dynamic;
  437.  
  438.     property Columns: TIvDBGridColumns read FColumns write SeTIvColumns;
  439.     property DefaultDrawing: Boolean read FDefaultDrawing write FDefaultDrawing default True;
  440.     property DataSource: TDataSource read GetDataSource write SetDataSource;
  441.     property DataLink: TIvGridDataLink read FDataLink;
  442.     property IndicatorOffset: Byte read FIndicatorOffset;
  443.     property LayoutLock: Byte read FLayoutLock;
  444.     property Options: TIvDBGridOptions read FOptions write SetOptions
  445.       default [dgEditing, dgTitles, dgIndicator, dgColumnResize, dgColLines,
  446.       dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit];
  447.     property ParentColor default False;
  448.     property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
  449.     property SelectedRows: TIvBookmarkList read FBookmarks;
  450.     property TitleFont: TFont read FTitleFont write SetTitleFont;
  451.     property UpdateLock: Byte read FUpdateLock;
  452.     property OnColEnter: TNotifyEvent read FOnColEnter write FOnColEnter;
  453.     property OnColExit: TNotifyEvent read FOnColExit write FOnColExit;
  454.     property OnDrawDataCell: TIvDrawDataCellEvent read FOnDrawDataCell
  455.       write FOnDrawDataCell; { obsolete }
  456.     property OnDrawColumnCell: TIvDrawColumnCellEvent read FOnDrawColumnCell
  457.       write FOnDrawColumnCell;
  458.     property OnEditButtonClick: TNotifyEvent read FOnEditButtonClick
  459.       write FOnEditButtonClick;
  460.     property OnColumnMoved: TIvMovedEvent read FOnColumnMoved write FOnColumnMoved;
  461.     property OnCellClick: TIvDBGridClickEvent read FOnCellClick write FOnCellClick;
  462.     property OnTitleClick: TIvDBGridClickEvent read FOnTitleClick write FOnTitleClick;
  463.  
  464.   public
  465.     constructor Create(AOwner: TComponent); override;
  466.     destructor Destroy; override;
  467.  
  468.     procedure DefaultDrawDataCell(const Rect: TRect; Field: TField;
  469.       State: TIvGridDrawState); { obsolete }
  470.     procedure DefaultDrawColumnCell(const Rect: TRect; DataCol: Integer;
  471.       Column: TIvColumn; State: TIvGridDrawState);
  472.     function ValidFieldIndex(FieldIndex: Integer): Boolean;
  473.  
  474.     property EditorMode;
  475.     property FieldCount: Integer read GetFieldCount;
  476.     property Fields[FieldIndex: Integer]: TField read GetFields;
  477.     property SelectedField: TField read GetSelectedField write SetSelectedField;
  478.     property SelectedIndex: Integer read GetSelectedIndex write SetSelectedIndex;
  479.   end;
  480.  
  481.   TIvDBGrid = class(TIvCustomDBGrid)
  482.   public
  483.     property Canvas;
  484.     property SelectedRows;
  485.     //property ColLocale;
  486.  
  487.   published
  488.     property Align;
  489.     property BorderStyle;
  490.     property Color;
  491.     property Columns stored False; //StoreColumns;
  492.     property Ctl3D;
  493.     property DataSource;
  494.     property DefaultDrawing;
  495.     property DragCursor;
  496.     property DragMode;
  497.     property Enabled;
  498.     property FixedColor;
  499.     property Font;
  500.     property Options;
  501.     property ParentColor;
  502.     property ParentCtl3D;
  503.     property ParentFont;
  504.     property ParentShowHint;
  505.     property PopupMenu;
  506.     property ReadOnly;
  507.     property ShowHint;
  508.     property TabOrder;
  509.     property TabStop;
  510.     property TitleFont;
  511.     property Visible;
  512. {$IFDEF IVIME}
  513.     property ImeMode;
  514.     property ImeName;
  515. {$ENDIF}
  516.     property OnCellClick;
  517.     property OnColEnter;
  518.     property OnColExit;
  519.     property OnColumnMoved;
  520.     property OnDrawDataCell;  { obsolete }
  521.     property OnDrawColumnCell;
  522.     property OnDblClick;
  523.     property OnDragDrop;
  524.     property OnDragOver;
  525.     property OnEditButtonClick;
  526.     property OnEndDrag;
  527.     property OnEnter;
  528.     property OnExit;
  529.     property OnKeyDown;
  530.     property OnKeyPress;
  531.     property OnKeyUp;
  532.     property OnStartDrag;
  533.     property OnTitleClick;
  534.   end;
  535.  
  536. const
  537.   IndicatorWidth = 11;
  538. {$ENDIF}
  539.  
  540. implementation
  541.  
  542. {$IFNDEF IVBIDI}
  543. uses
  544. {$IFNDEF IVWIDE}
  545.   BDE, DBTables,
  546. {$ENDIF}
  547.   DBConsts, Dialogs,
  548.   IvDictio, IvMlUtil;
  549.  
  550. {$R IVDBGRID.RES}
  551.  
  552. const
  553.   bmArrow = 'IVDBGARROW';
  554.   bmEdit = 'IVDBEDIT';
  555.   bmInsert = 'IVDBINSERT';
  556.   bmMultiDot = 'IVDBMULTIDOT';
  557.   bmMultiArrow = 'IVDBMULTIARROW';
  558.  
  559.   MaxMapSize = (MaxInt div 2) div SizeOf(Integer);  { 250 million }
  560.  
  561. { Error reporting }
  562.  
  563. {$IFDEF IVWIDE}
  564. procedure RaiseGridError(const S: String);
  565. begin
  566.   raise EIvInvalidGridOperation.Create(S);
  567. end;
  568. {$ELSE}
  569. procedure RaiseGridError(const id: Integer);
  570. begin
  571.   raise EIvInvalidGridOperation.CreateRes(id);
  572. end;
  573. {$ENDIF}
  574.  
  575. procedure KillMessage(Wnd: HWnd; Msg: Integer);
  576. // Delete the requested message from the queue, but throw back
  577. // any WM_QUIT msgs that PeekMessage may also return
  578. var
  579.   M: TMsg;
  580. begin
  581.   M.Message := 0;
  582.   if PeekMessage(M, Wnd, Msg, Msg, pm_Remove) and (M.Message = WM_QUIT) then
  583.     PostQuitMessage(M.wparam);
  584. end;
  585.  
  586. { TIvDBGridInplaceEdit }
  587.  
  588. { TIvDBGridInplaceEdit adds support for a button on the in-place editor,
  589.   which can be used to drop down a table-based lookup list, a stringlist-based
  590.   pick list, or (if button style is esEllipsis) fire the grid event
  591.   OnEditButtonClick.  }
  592.  
  593. type
  594.   TEditStyle = (esSimple, esEllipsis, esPickList, esDataList);
  595.   TPopupListbox = class;
  596.  
  597.   TIvDBGridInplaceEdit = class(TIvInplaceEdit)
  598.   private
  599.     FButtonWidth: Integer;
  600.     FDataList: TDBLookupListBox;
  601.     FPickList: TPopupListbox;
  602.     FActiveList: TWinControl;
  603.     FLookupSource: TDatasource;
  604.     FEditStyle: TEditStyle;
  605.     FListVisible: Boolean;
  606.     FTracking: Boolean;
  607.     FPressed: Boolean;
  608.     procedure ListMouseUp(Sender: TObject; Button: TMouseButton;
  609.       Shift: TShiftState; X, Y: Integer);
  610.     procedure SetEditStyle(Value: TEditStyle);
  611.     procedure StopTracking;
  612.     procedure TrackButton(X,Y: Integer);
  613.     procedure CMCancelMode(var Message: TCMCancelMode); message CM_CancelMode;
  614.     procedure WMCancelMode(var Message: TMessage); message WM_CancelMode;
  615.     procedure WMKillFocus(var Message: TMessage); message WM_KillFocus;
  616.     procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message wm_LButtonDblClk;
  617.     procedure WMPaint(var Message: TWMPaint); message wm_Paint;
  618.     procedure WMSetCursor(var Message: TWMSetCursor); message WM_SetCursor;
  619.   protected
  620.     procedure BoundsChanged; override;
  621.     procedure CloseUp(Accept: Boolean);
  622.     procedure DoDropDownKeys(var Key: Word; Shift: TShiftState);
  623.     procedure DropDown;
  624.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  625.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  626.       X, Y: Integer); override;
  627.     procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  628.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
  629.       X, Y: Integer); override;
  630.     procedure PaintWindow(DC: HDC); override;
  631.     procedure UpdateContents; override;
  632.     procedure WndProc(var Message: TMessage); override;
  633.     property  EditStyle: TEditStyle read FEditStyle write SetEditStyle;
  634.     property  ActiveList: TWinControl read FActiveList write FActiveList;
  635.     property  DataList: TDBLookupListBox read FDataList;
  636.     property  PickList: TPopupListbox read FPickList;
  637.   public
  638.     constructor Create(Owner: TComponent); override;
  639.   end;
  640.  
  641. { TPopupListbox }
  642.  
  643.   TPopupListbox = class(TCustomListbox)
  644.   private
  645.     FSearchText: String;
  646.     FSearchTickCount: Longint;
  647.   protected
  648.     procedure CreateParams(var Params: TCreateParams); override;
  649.     procedure CreateWnd; override;
  650.     procedure KeyPress(var Key: Char); override;
  651.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  652.   end;
  653.  
  654. procedure TPopupListBox.CreateParams(var Params: TCreateParams);
  655. begin
  656.   inherited CreateParams(Params);
  657.   with Params do
  658.   begin
  659.     Style := Style or WS_BORDER;
  660.     ExStyle := WS_EX_TOOLWINDOW or WS_EX_TOPMOST;
  661.     WindowClass.Style := CS_SAVEBITS;
  662.   end;
  663. end;
  664.  
  665. procedure TPopupListbox.CreateWnd;
  666. begin
  667.   inherited CreateWnd;
  668.   Windows.SetParent(Handle, 0);
  669.   CallWindowProc(DefWndProc, Handle, wm_SetFocus, 0, 0);
  670. end;
  671.  
  672. procedure TPopupListbox.Keypress(var Key: Char);
  673. var
  674.   TickCount: Integer;
  675. begin
  676.   case Key of
  677.     #8, #27: FSearchText := '';
  678.     #32..#255:
  679.       begin
  680.         TickCount := GetTickCount;
  681.         if TickCount - FSearchTickCount > 2000 then FSearchText := '';
  682.         FSearchTickCount := TickCount;
  683.         if Length(FSearchText) < 32 then FSearchText := FSearchText + Key;
  684.         SendMessage(Handle, LB_SelectString, WORD(-1), Longint(PChar(FSearchText)));
  685.         Key := #0;
  686.       end;
  687.   end;
  688.   inherited Keypress(Key);
  689. end;
  690.  
  691. procedure TPopupListbox.MouseUp(Button: TMouseButton; Shift: TShiftState;
  692.   X, Y: Integer);
  693. begin
  694.   inherited MouseUp(Button, Shift, X, Y);
  695.   TIvDBGridInPlaceEdit(Owner).CloseUp((X >= 0) and (Y >= 0) and
  696.       (X < Width) and (Y < Height));
  697. end;
  698.  
  699.  
  700. constructor TIvDBGridInplaceEdit.Create(Owner: TComponent);
  701. begin
  702.   inherited Create(Owner);
  703.   FLookupSource := TDataSource.Create(Self);
  704.   FButtonWidth := GetSystemMetrics(SM_CXVSCROLL);
  705.   FEditStyle := esSimple;
  706. end;
  707.  
  708. procedure TIvDBGridInplaceEdit.BoundsChanged;
  709. var
  710.   R: TRect;
  711. begin
  712.   SetRect(R, 2, 2, Width - 2, Height);
  713.   if FEditStyle <> esSimple then Dec(R.Right, FButtonWidth);
  714.   SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@R));
  715.   SendMessage(Handle, EM_SCROLLCARET, 0, 0);
  716. {$IFDEF IVWIDE}
  717.   if SysLocale.Fareast then
  718.     SetImeCompositionWindow(Font, R.Left, R.Top);
  719. {$ENDIF}
  720. end;
  721.  
  722. procedure TIvDBGridInplaceEdit.CloseUp(Accept: Boolean);
  723. var
  724.   MasterField: TField;
  725.   ListValue: Variant;
  726. begin
  727.   if FListVisible then
  728.   begin
  729.     if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
  730.     if FActiveList = FDataList then
  731.       ListValue := FDataList.KeyValue
  732.     else
  733.       if FPickList.ItemIndex <> -1 then
  734.         ListValue := FPickList.Items[FPicklist.ItemIndex];
  735.     SetWindowPos(FActiveList.Handle, 0, 0, 0, 0, 0, SWP_NOZORDER or
  736.       SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_HIDEWINDOW);
  737.     FListVisible := False;
  738.     if Assigned(FDataList) then
  739.       FDataList.ListSource := nil;
  740.     FLookupSource.Dataset := nil;
  741.     Invalidate;
  742.     if Accept then
  743.       if FActiveList = FDataList then
  744.         with TIvCustomDBGrid(Grid), Columns[SelectedIndex].Field do
  745.         begin
  746.           MasterField := DataSet.FieldByName(KeyFields);
  747.           if MasterField.CanModify then
  748.           begin
  749.             DataSet.Edit;
  750.             MasterField.Value := ListValue;
  751.           end;
  752.         end
  753.       else
  754.         if (not VarIsNull(ListValue)) and EditCanModify then
  755.           with TIvCustomDBGrid(Grid), Columns[SelectedIndex].Field do
  756.             Text := ListValue;
  757.   end;
  758. end;
  759.  
  760. procedure TIvDBGridInplaceEdit.DoDropDownKeys(var Key: Word; Shift: TShiftState);
  761. begin
  762.   case Key of
  763.     VK_UP, VK_DOWN:
  764.       if ssAlt in Shift then
  765.       begin
  766.         if FListVisible then CloseUp(True) else DropDown;
  767.         Key := 0;
  768.       end;
  769.     VK_RETURN, VK_ESCAPE:
  770.       if FListVisible and not (ssAlt in Shift) then
  771.       begin
  772.         CloseUp(Key = VK_RETURN);
  773.         Key := 0;
  774.       end;
  775.   end;
  776. end;
  777.  
  778. procedure TIvDBGridInplaceEdit.DropDown;
  779. var
  780.   P: TPoint;
  781.   I,J,Y: Integer;
  782.   Column: TIvColumn;
  783. begin
  784.   if not FListVisible and Assigned(FActiveList) then
  785.   begin
  786.     FActiveList.Width := Width;
  787.     with TIvCustomDBGrid(Grid) do
  788.       Column := Columns[SelectedIndex];
  789.     if FActiveList = FDataList then
  790.     with Column.Field do
  791.     begin
  792.       FDataList.Color := Color;
  793.       FDataList.Font := Font;
  794.       FDataList.RowCount := Column.DropDownRows;
  795.       FLookupSource.DataSet := LookupDataSet;
  796.       FDataList.KeyField := LookupKeyFields;
  797.       FDataList.ListField := LookupResultField;
  798.       FDataList.ListSource := FLookupSource;
  799.       FDataList.KeyValue := DataSet.FieldByName(KeyFields).Value;
  800. {      J := Column.DefaultWidth;
  801.       if J > FDataList.ClientWidth then
  802.         FDataList.ClientWidth := J;
  803. }    end
  804.     else
  805.     begin
  806.       FPickList.Color := Color;
  807.       FPickList.Font := Font;
  808.       FPickList.Items := Column.Picklist;
  809.       if FPickList.Items.Count >= Column.DropDownRows then
  810.         FPickList.Height := Column.DropDownRows * FPickList.ItemHeight + 4
  811.       else
  812.         FPickList.Height := FPickList.Items.Count * FPickList.ItemHeight + 4;
  813.       if Column.Field.IsNull then
  814.         FPickList.ItemIndex := -1
  815.       else
  816.         FPickList.ItemIndex := FPickList.Items.IndexOf(Column.Field.Value);
  817.       J := FPickList.ClientWidth;
  818.       for I := 0 to FPickList.Items.Count - 1 do
  819.       begin
  820.         Y := FPickList.Canvas.TextWidth(FPickList.Items[I]);
  821.         if Y > J then J := Y;
  822.       end;
  823.       FPickList.ClientWidth := J;
  824.     end;
  825.     P := Parent.ClientToScreen(Point(Left, Top));
  826.     Y := P.Y + Height;
  827.     if Y + FActiveList.Height > Screen.Height then Y := P.Y - FActiveList.Height;
  828.     SetWindowPos(FActiveList.Handle, HWND_TOP, P.X, Y, 0, 0,
  829.       SWP_NOSIZE or SWP_NOACTIVATE or SWP_SHOWWINDOW);
  830.     FListVisible := True;
  831.     Invalidate;
  832.     Windows.SetFocus(Handle);
  833.   end;
  834. end;
  835.  
  836. type
  837.   TWinControlCracker = class(TWinControl) end;
  838.  
  839. procedure TIvDBGridInplaceEdit.KeyDown(var Key: Word; Shift: TShiftState);
  840. begin
  841.   if (EditStyle = esEllipsis) and (Key = VK_RETURN) and (Shift = [ssCtrl]) then
  842.   begin
  843.     TIvCustomDBGrid(Grid).EditButtonClick;
  844.     KillMessage(Handle, WM_CHAR);
  845.   end
  846.   else
  847.     inherited KeyDown(Key, Shift);
  848. end;
  849.  
  850. procedure TIvDBGridInplaceEdit.ListMouseUp(Sender: TObject; Button: TMouseButton;
  851.   Shift: TShiftState; X, Y: Integer);
  852. begin
  853.   if Button = mbLeft then
  854.     CloseUp(PtInRect(FActiveList.ClientRect, Point(X, Y)));
  855. end;
  856.  
  857. procedure TIvDBGridInplaceEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
  858.   X, Y: Integer);
  859. begin
  860.   if (Button = mbLeft) and (FEditStyle <> esSimple) and
  861.     PtInRect(Rect(Width - FButtonWidth, 0, Width, Height), Point(X,Y)) then
  862.   begin
  863.     if FListVisible then
  864.       CloseUp(False)
  865.     else
  866.     begin
  867.       MouseCapture := True;
  868.       FTracking := True;
  869.       TrackButton(X, Y);
  870.       if Assigned(FActiveList) then
  871.         DropDown;
  872.     end;
  873.   end;
  874.   inherited MouseDown(Button, Shift, X, Y);
  875. end;
  876.  
  877. procedure TIvDBGridInplaceEdit.MouseMove(Shift: TShiftState; X, Y: Integer);
  878. var
  879.   ListPos: TPoint;
  880.   MousePos: TSmallPoint;
  881. begin
  882.   if FTracking then
  883.   begin
  884.     TrackButton(X, Y);
  885.     if FListVisible then
  886.     begin
  887.       ListPos := FActiveList.ScreenToClient(ClientToScreen(Point(X, Y)));
  888.       if PtInRect(FActiveList.ClientRect, ListPos) then
  889.       begin
  890.         StopTracking;
  891.         MousePos := PointToSmallPoint(ListPos);
  892.         SendMessage(FActiveList.Handle, WM_LBUTTONDOWN, 0, Integer(MousePos));
  893.         Exit;
  894.       end;
  895.     end;
  896.   end;
  897.   inherited MouseMove(Shift, X, Y);
  898. end;
  899.  
  900. procedure TIvDBGridInplaceEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
  901.   X, Y: Integer);
  902. var
  903.   WasPressed: Boolean;
  904. begin
  905.   WasPressed := FPressed;
  906.   StopTracking;
  907.   if (Button = mbLeft) and (FEditStyle = esEllipsis) and WasPressed then
  908.     TIvCustomDBGrid(Grid).EditButtonClick;
  909.   inherited MouseUp(Button, Shift, X, Y);
  910. end;
  911.  
  912. procedure TIvDBGridInplaceEdit.PaintWindow(DC: HDC);
  913. var
  914.   R: TRect;
  915.   Flags: Integer;
  916.   W: Integer;
  917. begin
  918.   if FEditStyle <> esSimple then
  919.   begin
  920.     SetRect(R, Width - FButtonWidth, 0, Width, Height);
  921.     Flags := 0;
  922.     if FEditStyle in [esDataList, esPickList] then
  923.     begin
  924.       if FActiveList = nil then
  925.         Flags := DFCS_INACTIVE
  926.       else if FPressed then
  927.         Flags := DFCS_FLAT or DFCS_PUSHED;
  928.       DrawFrameControl(DC, R, DFC_SCROLL, Flags or DFCS_SCROLLCOMBOBOX);
  929.     end
  930.     else   { esEllipsis }
  931.     begin
  932.       if FPressed then
  933.         Flags := BF_FLAT;
  934.       DrawEdge(DC, R, EDGE_RAISED, BF_RECT or BF_MIDDLE or Flags);
  935.       Flags := ((R.Right - R.Left) shr 1) - 1 + Ord(FPressed);
  936.       W := Height shr 3;
  937.       if W = 0 then W := 1;
  938.       PatBlt(DC, R.Left + Flags, R.Top + Flags, W, W, BLACKNESS);
  939.       PatBlt(DC, R.Left + Flags - (W * 2), R.Top + Flags, W, W, BLACKNESS);
  940.       PatBlt(DC, R.Left + Flags + (W * 2), R.Top + Flags, W, W, BLACKNESS);
  941.     end;
  942.     ExcludeClipRect(DC, R.Left, R.Top, R.Right, R.Bottom);
  943.   end;
  944.   inherited PaintWindow(DC);
  945. end;
  946.  
  947. procedure TIvDBGridInplaceEdit.SetEditStyle(Value: TEditStyle);
  948. begin
  949.   if Value = FEditStyle then Exit;
  950.   FEditStyle := Value;
  951.   case Value of
  952.     esPickList:
  953.       begin
  954.         if FPickList = nil then
  955.         begin
  956.           FPickList := TPopupListbox.Create(Self);
  957.           FPickList.Visible := False;
  958.           FPickList.Parent := Self;
  959.           FPickList.OnMouseUp := ListMouseUp;
  960.           FPickList.IntegralHeight := True;
  961.           FPickList.ItemHeight := 11;
  962.         end;
  963.         FActiveList := FPickList;
  964.       end;
  965.     esDataList:
  966.       begin
  967.         if FDataList = nil then
  968.         begin
  969.           FDataList := TPopupDataList.Create(Self);
  970.           FDataList.Visible := False;
  971.           FDataList.Parent := Self;
  972.           FDataList.OnMouseUp := ListMouseUp;
  973.         end;
  974.         FActiveList := FDataList;
  975.       end;
  976.   else  { cbsNone, cbsEllipsis, or read only field }
  977.     FActiveList := nil;
  978.   end;
  979.   with TIvCustomDBGrid(Grid) do
  980.     Self.ReadOnly := Columns[SelectedIndex].ReadOnly;
  981.   Repaint;
  982. end;
  983.  
  984. procedure TIvDBGridInplaceEdit.StopTracking;
  985. begin
  986.   if FTracking then
  987.   begin
  988.     TrackButton(-1, -1);
  989.     FTracking := False;
  990.     MouseCapture := False;
  991.   end;
  992. end;
  993.  
  994. procedure TIvDBGridInplaceEdit.TrackButton(X,Y: Integer);
  995. var
  996.   NewState: Boolean;
  997.   R: TRect;
  998. begin
  999.   SetRect(R, ClientWidth - FButtonWidth, 0, ClientWidth, ClientHeight);
  1000.   NewState := PtInRect(R, Point(X, Y));
  1001.   if FPressed <> NewState then
  1002.   begin
  1003.     FPressed := NewState;
  1004.     InvalidateRect(Handle, @R, False);
  1005.   end;
  1006. end;
  1007.  
  1008. procedure TIvDBGridInplaceEdit.UpdateContents;
  1009. var
  1010.   Column: TIvColumn;
  1011.   NewStyle: TEditStyle;
  1012.   MasterField: TField;
  1013. begin
  1014.   with TIvCustomDBGrid(Grid) do
  1015.     Column := Columns[SelectedIndex];
  1016.   NewStyle := esSimple;
  1017.   case Column.ButtonStyle of
  1018.    cbsEllipsis: NewStyle := esEllipsis;
  1019.    cbsAuto:
  1020.      if Assigned(Column.Field) then
  1021.      with Column.Field do
  1022.      begin
  1023.        { Show the dropdown button only if the field is editable }
  1024.        if FieldKind = fkLookup then
  1025.        begin
  1026.          MasterField := Dataset.FieldByName(KeyFields);
  1027.          { Column.DefaultReadonly will always be True for a lookup field.
  1028.            Test if Column.ReadOnly has been assigned a value of True }
  1029.          if Assigned(MasterField) and MasterField.CanModify and
  1030.            not ((cvReadOnly in Column.AssignedValues) and Column.ReadOnly) then
  1031.            with TIvCustomDBGrid(Grid) do
  1032.              if not ReadOnly and DataLink.Active and not Datalink.ReadOnly then
  1033.                NewStyle := esDataList
  1034.        end
  1035.        else
  1036.        if Assigned(Column.Picklist) and (Column.PickList.Count > 0) and
  1037.          not Column.Readonly then
  1038.          NewStyle := esPickList;
  1039.      end;
  1040.   end;
  1041.   EditStyle := NewStyle;
  1042.   inherited UpdateContents;
  1043. end;
  1044.  
  1045. procedure TIvDBGridInplaceEdit.CMCancelMode(var Message: TCMCancelMode);
  1046. begin
  1047.   if (Message.Sender <> Self) and (Message.Sender <> FActiveList) then
  1048.     CloseUp(False);
  1049. end;
  1050.  
  1051. procedure TIvDBGridInplaceEdit.WMCancelMode(var Message: TMessage);
  1052. begin
  1053.   StopTracking;
  1054.   inherited;
  1055. end;
  1056.  
  1057. procedure TIvDBGridInplaceEdit.WMKillFocus(var Message: TMessage);
  1058. begin
  1059. {$IFDEF IVIME}
  1060.   if SysLocale.FarEast then
  1061.   begin
  1062.     ImeName := Screen.DefaultIme;
  1063.     ImeMode := imDontCare;
  1064.   end;
  1065. {$ENDIF}
  1066.   inherited;
  1067.   CloseUp(False);
  1068. end;
  1069.  
  1070. procedure TIvDBGridInplaceEdit.WMLButtonDblClk(var Message: TWMLButtonDblClk);
  1071. begin
  1072.   with Message do
  1073.   if (FEditStyle <> esSimple) and
  1074.     PtInRect(Rect(Width - FButtonWidth, 0, Width, Height), Point(XPos, YPos)) then
  1075.     Exit;
  1076.   inherited;
  1077. end;
  1078.  
  1079. procedure TIvDBGridInplaceEdit.WMPaint(var Message: TWMPaint);
  1080. begin
  1081.   PaintHandler(Message);
  1082. end;
  1083.  
  1084. procedure TIvDBGridInplaceEdit.WMSetCursor(var Message: TWMSetCursor);
  1085. var
  1086.   P: TPoint;
  1087. begin
  1088.   GetCursorPos(P);
  1089.   if (FEditStyle <> esSimple) and
  1090.     PtInRect(Rect(Width - FButtonWidth, 0, Width, Height), ScreenToClient(P)) then
  1091.     Windows.SetCursor(LoadCursor(0, idc_Arrow))
  1092.   else
  1093.     inherited;
  1094. end;
  1095.  
  1096. procedure TIvDBGridInplaceEdit.WndProc(var Message: TMessage);
  1097. begin
  1098.   case Message.Msg of
  1099.     wm_KeyDown, wm_SysKeyDown, wm_Char:
  1100.       if EditStyle in [esPickList, esDataList] then
  1101.       with TWMKey(Message) do
  1102.       begin
  1103.         DoDropDownKeys(CharCode, KeyDataToShiftState(KeyData));
  1104.         if (CharCode <> 0) and FListVisible then
  1105.         begin
  1106.           with TMessage(Message) do
  1107.             SendMessage(FActiveList.Handle, Msg, WParam, LParam);
  1108.           Exit;
  1109.         end;
  1110.       end
  1111.   end;
  1112.   inherited;
  1113. end;
  1114.  
  1115.  
  1116. { TIvGridDataLink }
  1117.  
  1118. type
  1119.   TIntArray = array[0..MaxMapSize] of Integer;
  1120.   PIntArray = ^TIntArray;
  1121.  
  1122. constructor TIvGridDataLink.Create(AGrid: TIvCustomDBGrid);
  1123. begin
  1124.   inherited Create;
  1125.   FGrid := AGrid;
  1126. end;
  1127.  
  1128. destructor TIvGridDataLink.Destroy;
  1129. begin
  1130.   ClearMapping;
  1131.   inherited Destroy;
  1132. end;
  1133.  
  1134. function TIvGridDataLink.GetDefaultFields: Boolean;
  1135. var
  1136.   I: Integer;
  1137. begin
  1138.   Result := True;
  1139.   if DataSet <> nil then Result := DataSet.DefaultFields;
  1140.   if Result and SparseMap then
  1141.   for I := 0 to FFieldCount-1 do
  1142.     if PIntArray(FFieldMap)^[I] < 0 then
  1143.     begin
  1144.       Result := False;
  1145.       Exit;
  1146.     end;
  1147. end;
  1148.  
  1149. function TIvGridDataLink.GetFields(I: Integer): TField;
  1150. begin
  1151.   if (0 <= I) and (I < FFieldCount) and (PIntArray(FFieldMap)^[I] >= 0) then
  1152.     Result := DataSet.Fields[PIntArray(FFieldMap)^[I]]
  1153.   else
  1154.     Result := nil;
  1155. end;
  1156.  
  1157. function TIvGridDataLink.AddMapping(const FieldName: string): Boolean;
  1158. var
  1159.   Field: TField;
  1160.   NewSize: Integer;
  1161. begin
  1162.   Result := True;
  1163.   if FFieldCount >= MaxMapSize then
  1164.     RaiseGridError(STooManyColumns);
  1165.   if SparseMap then
  1166.     Field := DataSet.FindField(FieldName)
  1167.   else
  1168.     Field := DataSet.FieldByName(FieldName);
  1169.  
  1170.   if FFieldCount = FFieldMapSize then
  1171.   begin
  1172.     NewSize := FFieldMapSize;
  1173.     if NewSize = 0 then
  1174.       NewSize := 8
  1175.     else
  1176.       Inc(NewSize, NewSize);
  1177.     if (NewSize < FFieldCount) then
  1178.       NewSize := FFieldCount + 1;
  1179.     if (NewSize > MaxMapSize) then
  1180.       NewSize := MaxMapSize;
  1181.     ReallocMem(FFieldMap, NewSize * SizeOf(Integer));
  1182.     FFieldMapSize := NewSize;
  1183.   end;
  1184.   if Assigned(Field) then
  1185.   begin
  1186.     PIntArray(FFieldMap)^[FFieldCount] := Field.Index;
  1187.     Field.FreeNotification(FGrid);
  1188.   end
  1189.   else
  1190.     PIntArray(FFieldMap)^[FFieldCount] := -1;
  1191.   Inc(FFieldCount);
  1192. end;
  1193.  
  1194. procedure TIvGridDataLink.ActiveChanged;
  1195. begin
  1196.   FGrid.LinkActive(Active);
  1197. end;
  1198.  
  1199. procedure TIvGridDataLink.ClearMapping;
  1200. begin
  1201.   if FFieldMap <> nil then
  1202.   begin
  1203.     FreeMem(FFieldMap, FFieldMapSize * SizeOf(Integer));
  1204.     FFieldMap := nil;
  1205.     FFieldMapSize := 0;
  1206.     FFieldCount := 0;
  1207.   end;
  1208. end;
  1209.  
  1210. procedure TIvGridDataLink.Modified;
  1211. begin
  1212.   FModified := True;
  1213. end;
  1214.  
  1215. procedure TIvGridDataLink.DataSetChanged;
  1216. begin
  1217.   FGrid.DataChanged;
  1218.   FModified := False;
  1219. end;
  1220.  
  1221. procedure TIvGridDataLink.DataSetScrolled(Distance: Integer);
  1222. begin
  1223.   FGrid.Scroll(Distance);
  1224. end;
  1225.  
  1226. procedure TIvGridDataLink.LayoutChanged;
  1227. var
  1228.   SaveState: Boolean;
  1229. begin
  1230.   { FLayoutFromDataset determines whether default column width is forced to
  1231.     be at least wide enough for the column title.  }
  1232.   SaveState := FGrid.FLayoutFromDataset;
  1233.   FGrid.FLayoutFromDataset := True;
  1234.   try
  1235.     FGrid.LayoutChanged;
  1236.   finally
  1237.     FGrid.FLayoutFromDataset := SaveState;
  1238.   end;
  1239.   inherited LayoutChanged;
  1240. end;
  1241.  
  1242. procedure TIvGridDataLink.FocusControl(Field: TFieldRef);
  1243. begin
  1244.   if Assigned(Field) and Assigned(Field^) then
  1245.   begin
  1246.     FGrid.SelectedField := Field^;
  1247.     if (FGrid.SelectedField = Field^) and FGrid.AcquireFocus then
  1248.     begin
  1249.       Field^ := nil;
  1250.       FGrid.ShowEditor;
  1251.     end;
  1252.   end;
  1253. end;
  1254.  
  1255. procedure TIvGridDataLink.EditingChanged;
  1256. begin
  1257.   FGrid.EditingChanged;
  1258. end;
  1259.  
  1260. procedure TIvGridDataLink.RecordChanged(Field: TField);
  1261. begin
  1262.   FGrid.RecordChanged(Field);
  1263.   FModified := False;
  1264. end;
  1265.  
  1266. procedure TIvGridDataLink.UpdateData;
  1267. begin
  1268.   FInUpdateData := True;
  1269.   try
  1270.     if FModified then FGrid.UpdateData;
  1271.     FModified := False;
  1272.   finally
  1273.     FInUpdateData := False;
  1274.   end;
  1275. end;
  1276.  
  1277. function TIvGridDataLink.GetMappedIndex(ColIndex: Integer): Integer;
  1278. begin
  1279.   if (0 <= ColIndex) and (ColIndex < FFieldCount) then
  1280.     Result := PIntArray(FFieldMap)^[ColIndex]
  1281.   else
  1282.     Result := -1;
  1283. end;
  1284.  
  1285. procedure TIvGridDataLink.Reset;
  1286. begin
  1287.   if FModified then RecordChanged(nil) else Dataset.Cancel;
  1288. end;
  1289.  
  1290.  
  1291. { TIvColumnTitle }
  1292. constructor TIvColumnTitle.Create(Column: TIvColumn);
  1293. begin
  1294.   inherited Create;
  1295.   FColumn := Column;
  1296.   FFont := TFont.Create;
  1297.   FFont.Assign(DefaultFont);
  1298.   FFont.OnChange := FontChanged;
  1299. end;
  1300.  
  1301. destructor TIvColumnTitle.Destroy;
  1302. begin
  1303.   FFont.Free;
  1304.   inherited Destroy;
  1305. end;
  1306.  
  1307. procedure TIvColumnTitle.Assign(Source: TPersistent);
  1308. begin
  1309.   if Source is TIvColumnTitle then
  1310.   begin
  1311.     if cvTitleAlignment in TIvColumnTitle(Source).FColumn.FAssignedValues then
  1312.       Alignment := TIvColumnTitle(Source).Alignment;
  1313.     if cvTitleColor in TIvColumnTitle(Source).FColumn.FAssignedValues then
  1314.       Color := TIvColumnTitle(Source).Color;
  1315.     if cvTitleCaption in TIvColumnTitle(Source).FColumn.FAssignedValues then
  1316.       Caption := TIvColumnTitle(Source).Caption;
  1317.     if cvTitleFont in TIvColumnTitle(Source).FColumn.FAssignedValues then
  1318.       Font := TIvColumnTitle(Source).Font;
  1319.   end
  1320.   else
  1321.     inherited Assign(Source);
  1322. end;
  1323.  
  1324. function TIvColumnTitle.DefaultAlignment: TAlignment;
  1325. begin
  1326.   Result := taLeftJustify;
  1327. end;
  1328.  
  1329. function TIvColumnTitle.DefaultColor: TColor;
  1330. var
  1331.   Grid: TIvCustomDBGrid;
  1332. begin
  1333.   Grid := FColumn.GetGrid;
  1334.   if Assigned(Grid) then
  1335.     Result := Grid.FixedColor
  1336.   else
  1337.     Result := clBtnFace;
  1338. end;
  1339.  
  1340. function TIvColumnTitle.DefaultFont: TFont;
  1341. var
  1342.   Grid: TIvCustomDBGrid;
  1343. begin
  1344.   Grid := FColumn.GetGrid;
  1345.   if Assigned(Grid) then
  1346.     Result := Grid.TitleFont
  1347.   else
  1348.     Result := FColumn.Font;
  1349. end;
  1350.  
  1351. function TIvColumnTitle.DefaultCaption: string;
  1352. var
  1353.   Field: TField;
  1354. begin
  1355.   Field := FColumn.Field;
  1356.   if Assigned(Field) then
  1357.     Result := Field.DisplayName
  1358.   else
  1359.     Result := FColumn.FieldName;
  1360. end;
  1361.  
  1362. procedure TIvColumnTitle.FontChanged(Sender: TObject);
  1363. begin
  1364.   Include(FColumn.FAssignedValues, cvTitleFont);
  1365.   FColumn.Changed(True);
  1366. end;
  1367.  
  1368. function TIvColumnTitle.GetAlignment: TAlignment;
  1369. begin
  1370.   if cvTitleAlignment in FColumn.FAssignedValues then
  1371.     Result := FAlignment
  1372.   else
  1373.     Result := DefaultAlignment;
  1374. end;
  1375.  
  1376. function TIvColumnTitle.GetColor: TColor;
  1377. begin
  1378.   if cvTitleColor in FColumn.FAssignedValues then
  1379.     Result := FColor
  1380.   else
  1381.     Result := DefaultColor;
  1382. end;
  1383.  
  1384. function TIvColumnTitle.GetCaption: string;
  1385. begin
  1386.   if cvTitleCaption in FColumn.FAssignedValues then
  1387.     Result := FCaption
  1388.   else
  1389.     Result := DefaultCaption;
  1390. end;
  1391.  
  1392. function TIvColumnTitle.GetFont: TFont;
  1393. var
  1394.   Save: TNotifyEvent;
  1395.   Def: TFont;
  1396. begin
  1397.   if not (cvTitleFont in FColumn.FAssignedValues) then
  1398.   begin
  1399.     Def := DefaultFont;
  1400.     if (FFont.Handle <> Def.Handle) or (FFont.Color <> Def.Color) then
  1401.     begin
  1402.       Save := FFont.OnChange;
  1403.       FFont.OnChange := nil;
  1404.       FFont.Assign(DefaultFont);
  1405.       FFont.OnChange := Save;
  1406.     end;
  1407.   end;
  1408.   Result := FFont;
  1409. end;
  1410.  
  1411. function TIvColumnTitle.IsAlignmentStored: Boolean;
  1412. begin
  1413.   Result := (cvTitleAlignment in FColumn.FAssignedValues) and
  1414.     (FAlignment <> DefaultAlignment);
  1415. end;
  1416.  
  1417. function TIvColumnTitle.IsColorStored: Boolean;
  1418. begin
  1419.   Result := (cvTitleColor in FColumn.FAssignedValues) and
  1420.     (FColor <> DefaultColor);
  1421. end;
  1422.  
  1423. function TIvColumnTitle.IsFontStored: Boolean;
  1424. begin
  1425.   Result := (cvTitleFont in FColumn.FAssignedValues);
  1426. end;
  1427.  
  1428. function TIvColumnTitle.IsCaptionStored: Boolean;
  1429. begin
  1430.   Result := (cvTitleCaption in FColumn.FAssignedValues) and
  1431.     (FCaption <> DefaultCaption);
  1432. end;
  1433.  
  1434. procedure TIvColumnTitle.RefreshDefaultFont;
  1435. var
  1436.   Save: TNotifyEvent;
  1437. begin
  1438.   if (cvTitleFont in FColumn.FAssignedValues) then Exit;
  1439.   Save := FFont.OnChange;
  1440.   FFont.OnChange := nil;
  1441.   try
  1442.     FFont.Assign(DefaultFont);
  1443.   finally
  1444.     FFont.OnChange := Save;
  1445.   end;
  1446. end;
  1447.  
  1448. procedure TIvColumnTitle.RestoreDefaults;
  1449. var
  1450.   FontAssigned: Boolean;
  1451. begin
  1452.   FontAssigned := cvTitleFont in FColumn.FAssignedValues;
  1453.   FColumn.FAssignedValues := FColumn.FAssignedValues - ColumnTitleValues;
  1454.   FCaption := '';
  1455.   RefreshDefaultFont;
  1456.   { If font was assigned, changing it back to default may affect grid title
  1457.     height, and title height changes require layout and redraw of the grid. }
  1458.   FColumn.Changed(FontAssigned);
  1459. end;
  1460.  
  1461. procedure TIvColumnTitle.SetAlignment(Value: TAlignment);
  1462. begin
  1463.   if (cvTitleAlignment in FColumn.FAssignedValues) and (Value = FAlignment) then Exit;
  1464.   FAlignment := Value;
  1465.   Include(FColumn.FAssignedValues, cvTitleAlignment);
  1466.   FColumn.Changed(False);
  1467. end;
  1468.  
  1469. procedure TIvColumnTitle.SetColor(Value: TColor);
  1470. begin
  1471.   if (cvTitleColor in FColumn.FAssignedValues) and (Value = FColor) then Exit;
  1472.   FColor := Value;
  1473.   Include(FColumn.FAssignedValues, cvTitleColor);
  1474.   FColumn.Changed(False);
  1475. end;
  1476.  
  1477. procedure TIvColumnTitle.SetFont(Value: TFont);
  1478. begin
  1479.   FFont.Assign(Value);
  1480. end;
  1481.  
  1482. procedure TIvColumnTitle.SetCaption(const Value: string);
  1483. begin
  1484.   if (cvTitleCaption in FColumn.FAssignedValues) and (Value = FCaption) then Exit;
  1485.   FCaption := Value;
  1486.   Include(FColumn.FAssignedValues, cvTitleCaption);
  1487.   FColumn.Changed(False);
  1488. end;
  1489.  
  1490.  
  1491. { TIvColumn }
  1492.  
  1493. constructor TIvColumn.Create(Collection: TCollection);
  1494. var
  1495.   Grid: TIvCustomDBGrid;
  1496. begin
  1497.   Grid := nil;
  1498.   if Assigned(Collection) and (Collection is TIvDBGridColumns) then
  1499.     Grid := TIvDBGridColumns(Collection).Grid;
  1500.   if Assigned(Grid) then
  1501.     Grid.BeginLayout;
  1502.   try
  1503.     inherited Create(Collection);
  1504.     FDropDownRows := 7;
  1505.     FButtonStyle := cbsAuto;
  1506.     FFont := TFont.Create;
  1507.     FFont.Assign(DefaultFont);
  1508.     FFont.OnChange := FontChanged;
  1509. {$IFDEF IVIME}
  1510.     FImeMode := imDontCare;
  1511.     FImeName := Screen.DefaultIme;
  1512. {$ENDIF}
  1513.     FTitle := CreateTitle;
  1514.   finally
  1515.     if Assigned(Grid) then
  1516.       Grid.EndLayout;
  1517.   end;
  1518. end;
  1519.  
  1520. destructor TIvColumn.Destroy;
  1521. begin
  1522.   FTitle.Free;
  1523.   FFont.Free;
  1524.   FPickList.Free;
  1525.   inherited Destroy;
  1526. end;
  1527.  
  1528. procedure TIvColumn.Assign(Source: TPersistent);
  1529. begin
  1530.   if Source is TIvColumn then
  1531.   begin
  1532.     if Assigned(Collection) then Collection.BeginUpdate;
  1533.     try
  1534.       RestoreDefaults;
  1535.       FieldName := TIvColumn(Source).FieldName;
  1536.       if cvColor in TIvColumn(Source).AssignedValues then
  1537.         Color := TIvColumn(Source).Color;
  1538.       if cvWidth in TIvColumn(Source).AssignedValues then
  1539.         Width := TIvColumn(Source).Width;
  1540.       if cvFont in TIvColumn(Source).AssignedValues then
  1541.         Font := TIvColumn(Source).Font;
  1542. {$IFDEF IVIME}
  1543.       if cvImeMode in TIvColumn(Source).AssignedValues then
  1544.         ImeMode := TIvColumn(Source).ImeMode;
  1545.       if cvImeName in TIvColumn(Source).AssignedValues then
  1546.         ImeName := TIvColumn(Source).ImeName;
  1547. {$ENDIF}
  1548.       if cvAlignment in TIvColumn(Source).AssignedValues then
  1549.         Alignment := TIvColumn(Source).Alignment;
  1550.       if cvReadOnly in TIvColumn(Source).AssignedValues then
  1551.         ReadOnly := TIvColumn(Source).ReadOnly;
  1552.       Title := TIvColumn(Source).Title;
  1553.       DropDownRows := TIvColumn(Source).DropDownRows;
  1554.       ButtonStyle := TIvColumn(Source).ButtonStyle;
  1555.       PickList := TIvColumn(Source).PickList;
  1556.       PopupMenu := TIvColumn(Source).PopupMenu;
  1557.     finally
  1558.       if Assigned(Collection) then Collection.EndUpdate;
  1559.     end;
  1560.   end
  1561.   else
  1562.     inherited Assign(Source);
  1563. end;
  1564.  
  1565. function TIvColumn.CreateTitle: TIvColumnTitle;
  1566. begin
  1567.   Result := TIvColumnTitle.Create(Self);
  1568. end;
  1569.  
  1570. function TIvColumn.DefaultAlignment: TAlignment;
  1571. begin
  1572.   if Assigned(Field) then
  1573.     Result := FField.Alignment
  1574.   else
  1575.     Result := taLeftJustify;
  1576. end;
  1577.  
  1578. function TIvColumn.DefaultColor: TColor;
  1579. var
  1580.   Grid: TIvCustomDBGrid;
  1581. begin
  1582.   Grid := GetGrid;
  1583.   if Assigned(Grid) then
  1584.     Result := Grid.Color
  1585.   else
  1586.     Result := clWindow;
  1587. end;
  1588.  
  1589. function TIvColumn.DefaultFont: TFont;
  1590. var
  1591.   Grid: TIvCustomDBGrid;
  1592. begin
  1593.   Grid := GetGrid;
  1594.   if Assigned(Grid) then
  1595.     Result := Grid.Font
  1596.   else
  1597.     Result := FFont;
  1598. end;
  1599.  
  1600. {$IFDEF IVIME}
  1601. function TIvColumn.DefaultImeMode: TImeMode;
  1602. var
  1603.   Grid: TIvCustomDBGrid;
  1604. begin
  1605.   Grid := GetGrid;
  1606.   if Assigned(Grid) then
  1607.     Result := Grid.ImeMode
  1608.   else
  1609.     Result := FImeMode;
  1610. end;
  1611.  
  1612. function TIvColumn.DefaultImeName: TImeName;
  1613. var
  1614.   Grid: TIvCustomDBGrid;
  1615. begin
  1616.   Grid := GetGrid;
  1617.   if Assigned(Grid) then
  1618.     Result := Grid.ImeName
  1619.   else
  1620.     Result := FImeName;
  1621. end;
  1622. {$ENDIF}
  1623.  
  1624. function TIvColumn.DefaultReadOnly: Boolean;
  1625. var
  1626.   Grid: TIvCustomDBGrid;
  1627. begin
  1628.   Grid := GetGrid;
  1629.   Result := (Assigned(Grid) and Grid.ReadOnly) or (Assigned(Field) and FField.ReadOnly);
  1630. end;
  1631.  
  1632. function TIvColumn.DefaultWidth: Integer;
  1633. var
  1634.   W: Integer;
  1635.   RestoreCanvas: Boolean;
  1636.   TM: TTextMetric;
  1637. begin
  1638.   if GetGrid = nil then
  1639.   begin
  1640.     Result := 64;
  1641.     Exit;
  1642.   end;
  1643.   with GetGrid do
  1644.   begin
  1645.     if Assigned(Field) then
  1646.     begin
  1647.       RestoreCanvas := not HandleAllocated;
  1648.       if RestoreCanvas then
  1649.         Canvas.Handle := GetDC(0);
  1650.       try
  1651.         Canvas.Font := Self.Font;
  1652.         GetTextMetrics(Canvas.Handle, TM);
  1653.         Result := Field.DisplayWidth * (Canvas.TextWidth('0') - TM.tmOverhang)
  1654.           + TM.tmOverhang + 4;
  1655.         if dgTitles in Options then
  1656.         begin
  1657.           Canvas.Font := Title.Font;
  1658.           W := Canvas.TextWidth(Title.Caption) + 4;
  1659.           if Result < W then
  1660.             Result := W;
  1661.         end;
  1662.       finally
  1663.         if RestoreCanvas then
  1664.         begin
  1665.           ReleaseDC(0,Canvas.Handle);
  1666.           Canvas.Handle := 0;
  1667.         end;
  1668.       end;
  1669.     end
  1670.     else
  1671.       Result := DefaultColWidth;
  1672.   end;
  1673. end;
  1674.  
  1675. procedure TIvColumn.FontChanged;
  1676. begin
  1677.   Include(FAssignedValues, cvFont);
  1678.   Title.RefreshDefaultFont;
  1679.   Changed(False);
  1680. end;
  1681.  
  1682. function TIvColumn.GetAlignment: TAlignment;
  1683. begin
  1684.   if cvAlignment in FAssignedValues then
  1685.     Result := FAlignment
  1686.   else
  1687.     Result := DefaultAlignment;
  1688. end;
  1689.  
  1690. function TIvColumn.GetColor: TColor;
  1691. begin
  1692.   if cvColor in FAssignedValues then
  1693.     Result := FColor
  1694.   else
  1695.     Result := DefaultColor;
  1696. end;
  1697.  
  1698. function TIvColumn.GetField: TField;
  1699. var
  1700.   Grid: TIvCustomDBGrid;
  1701. begin    { Returns Nil if FieldName can't be found in dataset }
  1702.   Grid := GetGrid;
  1703.   if (FField = nil) and (Length(FFieldName) > 0) and Assigned(Grid) and
  1704.     Assigned(Grid.DataLink.DataSet) then
  1705.   with Grid.Datalink.Dataset do
  1706.     if Active or (not DefaultFields) then
  1707.       SetField(FindField(FieldName));
  1708.   Result := FField;
  1709. end;
  1710.  
  1711. function TIvColumn.GetFont: TFont;
  1712. var
  1713.   Save: TNotifyEvent;
  1714. begin
  1715.   if not (cvFont in FAssignedValues) and (FFont.Handle <> DefaultFont.Handle) then
  1716.   begin
  1717.     Save := FFont.OnChange;
  1718.     FFont.OnChange := nil;
  1719.     FFont.Assign(DefaultFont);
  1720.     FFont.OnChange := Save;
  1721.   end;
  1722.   Result := FFont;
  1723. end;
  1724.  
  1725. function TIvColumn.GetGrid: TIvCustomDBGrid;
  1726. begin
  1727.   if Assigned(Collection) and (Collection is TIvDBGridColumns) then
  1728.     Result := TIvDBGridColumns(Collection).Grid
  1729.   else
  1730.     Result := nil;
  1731. end;
  1732.  
  1733. {$IFDEF IVWIDE}
  1734. function TIvColumn.GetDisplayName: string;
  1735. begin
  1736.   Result := FFieldName;
  1737.   if Result = '' then Result := inherited GetDisplayName;
  1738. end;
  1739. {$ENDIF}
  1740.  
  1741. {$IFDEF IVIME}
  1742. function TIvColumn.GetImeMode: TImeMode;
  1743. begin
  1744.   if cvImeMode in FAssignedValues then
  1745.     Result := FImeMode
  1746.   else
  1747.     Result := DefaultImeMode;
  1748. end;
  1749.  
  1750. function TIvColumn.GetImeName: TImeName;
  1751. begin
  1752.   if cvImeName in FAssignedValues then
  1753.     Result := FImeName
  1754.   else
  1755.     Result := DefaultImeName;
  1756. end;
  1757. {$ENDIF}
  1758.  
  1759. function TIvColumn.GetPickList: TStrings;
  1760. begin
  1761.   if FPickList = nil then
  1762.     FPickList := TStringList.Create;
  1763.   Result := FPickList;
  1764. end;
  1765.  
  1766. function TIvColumn.GetReadOnly: Boolean;
  1767. begin
  1768.   if cvReadOnly in FAssignedValues then
  1769.     Result := FReadOnly
  1770.   else
  1771.     Result := DefaultReadOnly;
  1772. end;
  1773.  
  1774. function TIvColumn.GetWidth: Integer;
  1775. begin
  1776.   if cvWidth in FAssignedValues then
  1777.     Result := FWidth
  1778.   else
  1779.     Result := DefaultWidth;
  1780. end;
  1781.  
  1782. function TIvColumn.IsAlignmentStored: Boolean;
  1783. begin
  1784.   Result := (cvAlignment in FAssignedValues) and (FAlignment <> DefaultAlignment);
  1785. end;
  1786.  
  1787. function TIvColumn.IsColorStored: Boolean;
  1788. begin
  1789.   Result := (cvColor in FAssignedValues) and (FColor <> DefaultColor);
  1790. end;
  1791.  
  1792. function TIvColumn.IsFontStored: Boolean;
  1793. begin
  1794.   Result := (cvFont in FAssignedValues);
  1795. end;
  1796.  
  1797. {$IFDEF IVIME}
  1798. function TIvColumn.IsImeModeStored: Boolean;
  1799. begin
  1800.   Result := (cvImeMode in FAssignedValues) and (FImeMode <> DefaultImeMode);
  1801. end;
  1802.  
  1803. function TIvColumn.IsImeNameStored: Boolean;
  1804. begin
  1805.   Result := (cvImeName in FAssignedValues) and (FImeName <> DefaultImeName);
  1806. end;
  1807. {$ENDIF}
  1808.  
  1809. function TIvColumn.IsReadOnlyStored: Boolean;
  1810. begin
  1811.   Result := (cvReadOnly in FAssignedValues) and (FReadOnly <> DefaultReadOnly);
  1812. end;
  1813.  
  1814. function TIvColumn.IsWidthStored: Boolean;
  1815. begin
  1816.   Result := (cvWidth in FAssignedValues) and (FWidth <> DefaultWidth);
  1817. end;
  1818.  
  1819. procedure TIvColumn.RefreshDefaultFont;
  1820. var
  1821.   Save: TNotifyEvent;
  1822. begin
  1823.   if cvFont in FAssignedValues then Exit;
  1824.   Save := FFont.OnChange;
  1825.   FFont.OnChange := nil;
  1826.   try
  1827.     FFont.Assign(DefaultFont);
  1828.   finally
  1829.     FFont.OnChange := Save;
  1830.   end;
  1831. end;
  1832.  
  1833. procedure TIvColumn.RestoreDefaults;
  1834. var
  1835.   FontAssigned: Boolean;
  1836. begin
  1837.   FontAssigned := cvFont in FAssignedValues;
  1838.   FTitle.RestoreDefaults;
  1839.   FAssignedValues := [];
  1840.   RefreshDefaultFont;
  1841.   FPickList.Free;
  1842.   FPickList := nil;
  1843.   ButtonStyle := cbsAuto;
  1844.   Changed(FontAssigned);
  1845. end;
  1846.  
  1847. procedure TIvColumn.SetAlignment(Value: TAlignment);
  1848. begin
  1849.   if (cvAlignment in FAssignedValues) and (Value = FAlignment) then Exit;
  1850.   FAlignment := Value;
  1851.   Include(FAssignedValues, cvAlignment);
  1852.   Changed(False);
  1853. end;
  1854.  
  1855. procedure TIvColumn.SetButtonStyle(Value: TIvColumnButtonStyle);
  1856. begin
  1857.   if Value = FButtonStyle then Exit;
  1858.   FButtonStyle := Value;
  1859.   Changed(False);
  1860. end;
  1861.  
  1862. procedure TIvColumn.SetColor(Value: TColor);
  1863. begin
  1864.   if (cvColor in FAssignedValues) and (Value = FColor) then Exit;
  1865.   FColor := Value;
  1866.   Include(FAssignedValues, cvColor);
  1867.   Changed(False);
  1868. end;
  1869.  
  1870. procedure TIvColumn.SetField(Value: TField);
  1871. begin
  1872.   if FField = Value then Exit;
  1873.   FField := Value;
  1874.   if Assigned(Value) then
  1875.     FFieldName := Value.FieldName;
  1876.   Changed(False);
  1877. end;
  1878.  
  1879. procedure TIvColumn.SetFieldName(const Value: String);
  1880. var
  1881.   AField: TField;
  1882.   Grid: TIvCustomDBGrid;
  1883. begin
  1884.   AField := nil;
  1885.   Grid := GetGrid;
  1886.   if Assigned(Grid) and Assigned(Grid.DataLink.DataSet) and
  1887.     not (csLoading in Grid.ComponentState) and (Length(Value) > 0) then
  1888.       AField := Grid.DataLink.DataSet.FindField(Value); { no exceptions }
  1889.   FFieldName := Value;
  1890.   SetField(AField);
  1891.   Changed(False);
  1892. end;
  1893.  
  1894. procedure TIvColumn.SetFont(Value: TFont);
  1895. begin
  1896.   FFont.Assign(Value);
  1897.   Include(FAssignedValues, cvFont);
  1898.   Changed(False);
  1899. end;
  1900.  
  1901. {$IFDEF IVIME}
  1902. procedure TIvColumn.SetImeMode(Value: TImeMode);
  1903. begin
  1904.   if (cvImeMode in FAssignedValues) or (Value <> DefaultImeMode) then
  1905.   begin
  1906.     FImeMode := Value;
  1907.     Include(FAssignedValues, cvImeMode);
  1908.   end;
  1909.   Changed(False);
  1910. end;
  1911.  
  1912. procedure TIvColumn.SetImeName(Value: TImeName);
  1913. begin
  1914.   if (cvImeName in FAssignedValues) or (Value <> DefaultImeName) then
  1915.   begin
  1916.     FImeName := Value;
  1917.     Include(FAssignedValues, cvImeName);
  1918.   end;
  1919.   Changed(False);
  1920. end;
  1921. {$ENDIF}
  1922.  
  1923. procedure TIvColumn.SetPickList(Value: TStrings);
  1924. begin
  1925.   if Value = nil then
  1926.   begin
  1927.     FPickList.Free;
  1928.     FPickList := nil;
  1929.     Exit;
  1930.   end;
  1931.   PickList.Assign(Value);
  1932. end;
  1933.  
  1934. procedure TIvColumn.SetPopupMenu(Value: TPopupMenu);
  1935. begin
  1936.   FPopupMenu := Value;
  1937.   if Value <> nil then Value.FreeNotification(GetGrid);
  1938. end;
  1939.  
  1940. procedure TIvColumn.SetReadOnly(Value: Boolean);
  1941. begin
  1942.   if (cvReadOnly in FAssignedValues) and (Value = FReadOnly) then Exit;
  1943.   FReadOnly := Value;
  1944.   Include(FAssignedValues, cvReadOnly);
  1945.   Changed(False);
  1946. end;
  1947.  
  1948. procedure TIvColumn.SetTitle(Value: TIvColumnTitle);
  1949. begin
  1950.   FTitle.Assign(Value);
  1951. end;
  1952.  
  1953. procedure TIvColumn.SetWidth(Value: Integer);
  1954. begin
  1955.   if (cvWidth in FAssignedValues) or (Value <> DefaultWidth) then
  1956.   begin
  1957.     FWidth := Value;
  1958.     Include(FAssignedValues, cvWidth);
  1959.   end;
  1960.   Changed(False);
  1961. end;
  1962.  
  1963. { TPassthroughColumn }
  1964.  
  1965. type
  1966.   TPassthroughColumnTitle = class(TIvColumnTitle)
  1967.   private
  1968.     procedure SetCaption(const Value: string); override;
  1969.   end;
  1970.  
  1971.   TPassthroughColumn = class(TIvColumn)
  1972.   private
  1973.     procedure SetAlignment(Value: TAlignment); override;
  1974.     procedure SetField(Value: TField); override;
  1975.     procedure SetIndex(Value: Integer); override;
  1976.     procedure SetReadOnly(Value: Boolean); override;
  1977.     procedure SetWidth(Value: Integer); override;
  1978.   protected
  1979.     function CreateTitle: TIvColumnTitle; override;
  1980.   end;
  1981.  
  1982. { TPassthroughColumnTitle }
  1983.  
  1984. procedure TPassthroughColumnTitle.SetCaption(const Value: string);
  1985. var
  1986.   Grid: TIvCustomDBGrid;
  1987. begin
  1988.   Grid := FColumn.GetGrid;
  1989.   if Assigned(Grid) and (Grid.Datalink.Active) and Assigned(FColumn.Field) then
  1990.     FColumn.Field.DisplayLabel := Value
  1991.   else
  1992.     inherited SetCaption(Value);
  1993. end;
  1994.  
  1995.  
  1996. { TPassthroughColumn }
  1997.  
  1998. function TPassthroughColumn.CreateTitle: TIvColumnTitle;
  1999. begin
  2000.   Result := TPassthroughColumnTitle.Create(Self);
  2001. end;
  2002.  
  2003. procedure TPassthroughColumn.SetAlignment(Value: TAlignment);
  2004. var
  2005.   Grid: TIvCustomDBGrid;
  2006. begin
  2007.   Grid := GetGrid;
  2008.   if Assigned(Grid) and (Grid.Datalink.Active) and Assigned(Field) then
  2009.     Field.Alignment := Value
  2010.   else
  2011.     inherited SetAlignment(Value);
  2012. end;
  2013.  
  2014. procedure TPassthroughColumn.SetField(Value: TField);
  2015. begin
  2016.   inherited SetField(Value);
  2017.   if Value = nil then
  2018.     FFieldName := '';
  2019.   RestoreDefaults;
  2020. end;
  2021.  
  2022. procedure TPassthroughColumn.SetIndex(Value: Integer);
  2023. var
  2024.   Grid: TIvCustomDBGrid;
  2025.   Fld: TField;
  2026. begin
  2027.   Grid := GetGrid;
  2028.   if Assigned(Grid) and Grid.Datalink.Active then
  2029.   begin
  2030.     Fld := Grid.Datalink.Fields[Value];
  2031.     if Assigned(Fld) then
  2032.       Field.Index := Fld.Index;
  2033.   end;
  2034.   inherited SetIndex(Value);
  2035. end;
  2036.  
  2037. procedure TPassthroughColumn.SetReadOnly(Value: Boolean);
  2038. var
  2039.   Grid: TIvCustomDBGrid;
  2040. begin
  2041.   Grid := GetGrid;
  2042.   if Assigned(Grid) and Grid.Datalink.Active and Assigned(Field) then
  2043.     Field.ReadOnly := Value
  2044.   else
  2045.     inherited SetReadOnly(Value);
  2046. end;
  2047.  
  2048. procedure TPassthroughColumn.SetWidth(Value: Integer);
  2049. var
  2050.   Grid: TIvCustomDBGrid;
  2051.   TM: TTextMetric;
  2052. begin
  2053.   Grid := GetGrid;
  2054.   if Assigned(Grid) then
  2055.   begin
  2056.     if Grid.HandleAllocated and Assigned(Field) and Grid.FUpdateFields then
  2057.     with Grid do
  2058.     begin
  2059.       Canvas.Font := Self.Font;
  2060.       GetTextMetrics(Canvas.Handle, TM);
  2061.       Field.DisplayWidth := (Value + (TM.tmAveCharWidth div 2) - TM.tmOverhang - 3)
  2062.         div TM.tmAveCharWidth;
  2063.     end;
  2064.     if (not Grid.FLayoutFromDataset) or (cvWidth in FAssignedValues) then
  2065.       inherited SetWidth(Value);
  2066.   end
  2067.   else
  2068.     inherited SetWidth(Value);
  2069. end;
  2070.  
  2071.  
  2072. { TIvDBGridColumns }
  2073.  
  2074. constructor TIvDBGridColumns.Create(Grid: TIvCustomDBGrid; ColumnClass: TIvColumnClass);
  2075. begin
  2076.   inherited Create(ColumnClass);
  2077.   FGrid := Grid;
  2078. end;
  2079.  
  2080. function TIvDBGridColumns.Add: TIvColumn;
  2081. begin
  2082.   Result := TIvColumn(inherited Add);
  2083. end;
  2084.  
  2085. function TIvDBGridColumns.GeTIvColumn(Index: Integer): TIvColumn;
  2086. begin
  2087.   Result := TIvColumn(inherited Items[Index]);
  2088. end;
  2089.  
  2090. {$IFDEF IVWIDE}
  2091. function TIvDBGridColumns.GetOwner: TPersistent;
  2092. begin
  2093.   Result := FGrid;
  2094. end;
  2095. {$ENDIF}
  2096.  
  2097. function TIvDBGridColumns.GetState: TIvDBGridColumnsState;
  2098. begin
  2099.   Result := TIvDBGridColumnsState((Count > 0) and not (Items[0] is TPassthroughColumn));
  2100. end;
  2101.  
  2102. procedure TIvDBGridColumns.LoadFromFile(const Filename: string);
  2103. var
  2104.   S: TFileStream;
  2105. begin
  2106.   S := TFileStream.Create(Filename, fmOpenRead);
  2107.   try
  2108.     LoadFromStream(S);
  2109.   finally
  2110.     S.Free;
  2111.   end;
  2112. end;
  2113.  
  2114. type
  2115.   TIvColumnsWrapper = class(TComponent)
  2116.   private
  2117.     FColumns: TIvDBGridColumns;
  2118.   published
  2119.     property Columns: TIvDBGridColumns read FColumns write FColumns;
  2120.   end;
  2121.  
  2122. procedure TIvDBGridColumns.LoadFromStream(S: TStream);
  2123. var
  2124.   Wrapper: TIvColumnsWrapper;
  2125. begin
  2126.   Wrapper := TIvColumnsWrapper.Create(nil);
  2127.   try
  2128.     Wrapper.Columns := FGrid.CreateColumns;
  2129.     S.ReadComponent(Wrapper);
  2130.     Assign(Wrapper.Columns);
  2131.   finally
  2132.     Wrapper.Columns.Free;
  2133.     Wrapper.Free;
  2134.   end;
  2135. end;
  2136.  
  2137. procedure TIvDBGridColumns.RestoreDefaults;
  2138. var
  2139.   I: Integer;
  2140. begin
  2141.   BeginUpdate;
  2142.   try
  2143.     for I := 0 to Count-1 do
  2144.       Items[I].RestoreDefaults;
  2145.   finally
  2146.     EndUpdate;
  2147.   end;
  2148. end;
  2149.  
  2150. procedure TIvDBGridColumns.RebuildColumns;
  2151. var
  2152.   I: Integer;
  2153. begin
  2154.   if Assigned(FGrid) and Assigned(FGrid.DataSource) and
  2155.     Assigned(FGrid.Datasource.Dataset) then
  2156.   begin
  2157.     FGrid.BeginLayout;
  2158.     try
  2159.       Clear;
  2160.       with FGrid.Datasource.Dataset do
  2161.         for I := 0 to FieldCount-1 do
  2162.           Add.FieldName := Fields[I].FieldName
  2163.     finally
  2164.       FGrid.EndLayout;
  2165.     end
  2166.   end
  2167.   else
  2168.     Clear;
  2169. end;
  2170.  
  2171. procedure TIvDBGridColumns.SaveToFile(const Filename: string);
  2172. var
  2173.   S: TStream;
  2174. begin
  2175.   S := TFileStream.Create(Filename, fmCreate);
  2176.   try
  2177.     SaveToStream(S);
  2178.   finally
  2179.     S.Free;
  2180.   end;
  2181. end;
  2182.  
  2183. procedure TIvDBGridColumns.SaveToStream(S: TStream);
  2184. var
  2185.   Wrapper: TIvColumnsWrapper;
  2186. begin
  2187.   Wrapper := TIvColumnsWrapper.Create(nil);
  2188.   try
  2189.     Wrapper.Columns := Self;
  2190.     S.WriteComponent(Wrapper);
  2191.   finally
  2192.     Wrapper.Free;
  2193.   end;
  2194. end;
  2195.  
  2196. procedure TIvDBGridColumns.SeTIvColumn(Index: Integer; Value: TIvColumn);
  2197. begin
  2198.   Items[Index].Assign(Value);
  2199. end;
  2200.  
  2201. procedure TIvDBGridColumns.SetState(NewState: TIvDBGridColumnsState);
  2202. begin
  2203.   if NewState = State then Exit;
  2204.   if NewState = csDefault then
  2205.     Clear
  2206.   else
  2207.     RebuildColumns;
  2208. end;
  2209.  
  2210. procedure TIvDBGridColumns.Update(Item: TCollectionItem);
  2211. var
  2212.   Raw: Integer;
  2213. begin
  2214.   if (FGrid = nil) or (csLoading in FGrid.ComponentState) then Exit;
  2215.   if Item = nil then
  2216.   begin
  2217.     FGrid.LayoutChanged;
  2218.   end
  2219.   else
  2220.   begin
  2221.     Raw := FGrid.DataToRawColumn(Item.Index);
  2222.     FGrid.InvalidateCol(Raw);
  2223.     FGrid.ColWidths[Raw] := TIvColumn(Item).Width;
  2224.   end;
  2225. end;
  2226.  
  2227. { TIvBookmarkList }
  2228.  
  2229. constructor TIvBookmarkList.Create(AGrid: TIvCustomDBGrid);
  2230. begin
  2231.   inherited Create;
  2232.   FList := TStringList.Create;
  2233.   FList.OnChange := StringsChanged;
  2234.   FGrid := AGrid;
  2235. end;
  2236.  
  2237. destructor TIvBookmarkList.Destroy;
  2238. begin
  2239.   Clear;
  2240.   FList.Free;
  2241.   inherited Destroy;
  2242. end;
  2243.  
  2244. procedure TIvBookmarkList.Clear;
  2245. begin
  2246.   if FList.Count = 0 then Exit;
  2247.   FList.Clear;
  2248.   FGrid.Invalidate;
  2249. end;
  2250.  
  2251. function TIvBookmarkList.Compare(const Item1, Item2: TBookmarkStr): Integer;
  2252. {$IFNDEF IVWIDE}
  2253.   const Filter: array[Boolean, Boolean] of ShortInt = ((2,-1),(1,0));
  2254. {$ENDIF}
  2255. begin
  2256. {$IFDEF IVWIDE}
  2257.   with FGrid.Datalink.Datasource.Dataset do
  2258.     Result := CompareBookmarks(TBookmark(Item1), TBookmark(Item2));
  2259. {$ELSE}
  2260.   Result := Filter[Length(Item1) = 0, Length(Item2) = 0];
  2261.   if Result < 2 then
  2262.     Exit;
  2263.   with FGrid.Datalink.Datasource.Dataset do
  2264.     DB.Check(DbiCompareBookmarks(Handle, Pointer(Item1), Pointer(Item2), Result));
  2265.   if Result = 2 then
  2266.     Result := 0;
  2267. {$ENDIF}
  2268. end;
  2269.  
  2270. function TIvBookmarkList.CurrentRow: TBookmarkStr;
  2271. begin
  2272.   if not FLinkActive then RaiseGridError(sDataSetClosed);
  2273.   Result := FGrid.Datalink.Datasource.Dataset.Bookmark;
  2274. end;
  2275.  
  2276. function TIvBookmarkList.GetCurrentRowSelected: Boolean;
  2277. var
  2278.   Index: Integer;
  2279. begin
  2280.   Result := Find(CurrentRow, Index);
  2281. end;
  2282.  
  2283. function TIvBookmarkList.Find(const Item: TBookmarkStr; var Index: Integer): Boolean;
  2284. var
  2285.   L, H, I, C: Integer;
  2286. begin
  2287.   if (Item = FCache) and (FCacheIndex >= 0) then
  2288.   begin
  2289.     Index := FCacheIndex;
  2290.     Result := FCacheFind;
  2291.     Exit;
  2292.   end;
  2293.   Result := False;
  2294.   L := 0;
  2295.   H := FList.Count - 1;
  2296.   while L <= H do
  2297.   begin
  2298.     I := (L + H) shr 1;
  2299.     C := Compare(FList[I], Item);
  2300.     if C < 0 then L := I + 1 else
  2301.     begin
  2302.       H := I - 1;
  2303.       if C = 0 then
  2304.       begin
  2305.         Result := True;
  2306.         L := I;
  2307.       end;
  2308.     end;
  2309.   end;
  2310.   Index := L;
  2311.   FCache := Item;
  2312.   FCacheIndex := Index;
  2313.   FCacheFind := Result;
  2314. end;
  2315.  
  2316. function TIvBookmarkList.GetCount: Integer;
  2317. begin
  2318.   Result := FList.Count;
  2319. end;
  2320.  
  2321. function TIvBookmarkList.GetItem(Index: Integer): TBookmarkStr;
  2322. begin
  2323.   Result := FList[Index];
  2324. end;
  2325.  
  2326. function TIvBookmarkList.IndexOf(const Item: TBookmarkStr): Integer;
  2327. begin
  2328.   if not Find(Item, Result) then
  2329.     Result := -1;
  2330. end;
  2331.  
  2332. procedure TIvBookmarkList.LinkActive(Value: Boolean);
  2333. begin
  2334.   Clear;
  2335.   FLinkActive := Value;
  2336. end;
  2337.  
  2338. procedure TIvBookmarkList.Delete;
  2339. var
  2340.   I: Integer;
  2341. begin
  2342.   with FGrid.Datalink.Datasource.Dataset do
  2343.   begin
  2344.     DisableControls;
  2345.     try
  2346.       for I := FList.Count-1 downto 0 do
  2347.       begin
  2348.         Bookmark := FList[I];
  2349.         Delete;
  2350.         FList.Delete(I);
  2351.       end;
  2352.     finally
  2353.       EnableControls;
  2354.     end;
  2355.   end;
  2356. end;
  2357.  
  2358. function TIvBookmarkList.Refresh: Boolean;
  2359. var
  2360.   I: Integer;
  2361. begin
  2362.   Result := False;
  2363.   with FGrid.DataLink.Datasource.Dataset do
  2364.   try
  2365.     CheckBrowseMode;
  2366.     for I := FList.Count - 1 downto 0 do
  2367. {$IFDEF IVWIDE}
  2368.       if not BookmarkValid(TBookmark(FList[I])) then
  2369. {$ELSE}
  2370.       if DBISetToBookmark(Handle, Pointer(FList[I])) <> 0 then
  2371. {$ENDIF}
  2372.       begin
  2373.         Result := True;
  2374.         FList.Delete(I);
  2375.       end;
  2376.   finally
  2377.     UpdateCursorPos;
  2378.     if Result then FGrid.Invalidate;
  2379.   end;
  2380. end;
  2381.  
  2382. procedure TIvBookmarkList.SetCurrentRowSelected(Value: Boolean);
  2383. var
  2384.   Index: Integer;
  2385.   Current: TBookmarkStr;
  2386. begin
  2387.   Current := CurrentRow;
  2388.   if (Length(Current) = 0) or (Find(Current, Index) = Value) then Exit;
  2389.   if Value then
  2390.     FList.Insert(Index, Current)
  2391.   else
  2392.     FList.Delete(Index);
  2393.   FGrid.InvalidateRow(FGrid.Row);
  2394. end;
  2395.  
  2396. procedure TIvBookmarkList.StringsChanged(Sender: TObject);
  2397. begin
  2398.   FCache := '';
  2399.   FCacheIndex := -1;
  2400. end;
  2401.  
  2402.  
  2403. { TIvCustomDBGrid }
  2404.  
  2405. var
  2406.   DrawBitmap: TBitmap;
  2407.   UserCount: Integer;
  2408.  
  2409. procedure UsesBitmap;
  2410. begin
  2411.   if UserCount = 0 then
  2412.     DrawBitmap := TBitmap.Create;
  2413.   Inc(UserCount);
  2414. end;
  2415.  
  2416. procedure ReleaseBitmap;
  2417. begin
  2418.   Dec(UserCount);
  2419.   if UserCount = 0 then DrawBitmap.Free;
  2420. end;
  2421.  
  2422. function Max(X, Y: Integer): Integer;
  2423. begin
  2424.   Result := Y;
  2425.   if X > Y then Result := X;
  2426. end;
  2427.  
  2428. procedure WriteText(
  2429.   ACanvas: TCanvas;
  2430.   ARect: TRect;
  2431.   DX, DY: Integer;
  2432.   const Text: String;
  2433.   Alignment: TAlignment;
  2434.   bidi: Boolean);
  2435. const
  2436.   AlignFlags : array [TAlignment] of Integer =
  2437.     ( DT_LEFT or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,
  2438.       DT_RIGHT or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,
  2439.       DT_CENTER or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX );
  2440. var
  2441.   B, R: TRect;
  2442.   I, Left, flags: Integer;
  2443. begin
  2444.   I := ColorToRGB(ACanvas.Brush.Color);
  2445.   if GetNearestColor(ACanvas.Handle, I) = I then
  2446.   begin                       { Use ExtTextOut for solid colors }
  2447.     case Alignment of
  2448.       taLeftJustify:
  2449.         if bidi then
  2450.           Left := ARect.Right - ACanvas.TextWidth(Text) - 3
  2451.         else
  2452.           Left := ARect.Left + DX;
  2453.  
  2454.       taRightJustify:
  2455.         if bidi then
  2456.           Left := ARect.Left + DX
  2457.         else
  2458.           Left := ARect.Right - ACanvas.TextWidth(Text) - 3;
  2459.     else
  2460.       { taCenter }
  2461.       Left := ARect.Left + (ARect.Right - ARect.Left) shr 1
  2462.         - (ACanvas.TextWidth(Text) shr 1);
  2463.     end;
  2464.  
  2465.     flags := ETO_OPAQUE or ETO_CLIPPED;
  2466.     ExtTextOut(
  2467.       ACanvas.Handle,
  2468.       Left,
  2469.       ARect.Top + DY,
  2470.       flags,
  2471.       @ARect,
  2472.       PChar(Text),
  2473.       Length(Text),
  2474.       nil);
  2475.   end
  2476.   else
  2477.   begin
  2478.     { Use FillRect and Drawtext for dithered colors }
  2479.  
  2480. {$IFDEF IVWIDE}
  2481.     DrawBitmap.Canvas.Lock;
  2482. {$ENDIF}
  2483.     try
  2484.       with DrawBitmap, ARect do { Use offscreen bitmap to eliminate flicker and }
  2485.       begin                     { brush origin tics in painting / scrolling.    }
  2486.         Width := Max(Width, Right - Left);
  2487.         Height := Max(Height, Bottom - Top);
  2488.         R := Rect(DX, DY, Right - Left - 1, Bottom - Top - 1);
  2489.         B := Rect(0, 0, Right - Left, Bottom - Top);
  2490.       end;
  2491.       with DrawBitmap.Canvas do
  2492.       begin
  2493.         Font := ACanvas.Font;
  2494.         Font.Color := ACanvas.Font.Color;
  2495.         Brush := ACanvas.Brush;
  2496.         Brush.Style := bsSolid;
  2497.         FillRect(B);
  2498.         SetBkMode(Handle, TRANSPARENT);
  2499.  
  2500.         flags := AlignFlags[Alignment];
  2501.         DrawText(Handle, PChar(Text), Length(Text), R, flags);
  2502.       end;
  2503.       ACanvas.CopyRect(ARect, DrawBitmap.Canvas, B);
  2504.     finally
  2505. {$IFDEF IVWIDE}
  2506.       DrawBitmap.Canvas.Unlock;
  2507. {$ENDIF}
  2508.     end;
  2509.   end;
  2510. end;
  2511.  
  2512. constructor TIvCustomDBGrid.Create(AOwner: TComponent);
  2513. var
  2514.   Bmp: TBitmap;
  2515. begin
  2516.   inherited Create(AOwner);
  2517.   inherited DefaultDrawing := False;
  2518.   FAcquireFocus := True;
  2519.   Bmp := TBitmap.Create;
  2520.   try
  2521.     Bmp.LoadFromResourceName(HInstance, bmArrow);
  2522.     FIndicators := TImageList.CreateSize(Bmp.Width, Bmp.Height);
  2523.     FIndicators.AddMasked(Bmp, clWhite);
  2524.     Bmp.LoadFromResourceName(HInstance, bmEdit);
  2525.     FIndicators.AddMasked(Bmp, clWhite);
  2526.     Bmp.LoadFromResourceName(HInstance, bmInsert);
  2527.     FIndicators.AddMasked(Bmp, clWhite);
  2528.     Bmp.LoadFromResourceName(HInstance, bmMultiDot);
  2529.     FIndicators.AddMasked(Bmp, clWhite);
  2530.     Bmp.LoadFromResourceName(HInstance, bmMultiArrow);
  2531.     FIndicators.AddMasked(Bmp, clWhite);
  2532.   finally
  2533.     Bmp.Free;
  2534.   end;
  2535.   FTitleOffset := 1;
  2536.   FIndicatorOffset := 1;
  2537.   FUpdateFields := True;
  2538.   FOptions := [dgEditing, dgTitles, dgIndicator, dgColumnResize,
  2539.     dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit];
  2540.   DesignOptionsBoost := [goColSizing];
  2541.   VirtualView := True;
  2542.   UsesBitmap;
  2543.   ScrollBars := ssHorizontal;
  2544.   inherited Options := [goFixedHorzLine, goFixedVertLine, goHorzLine,
  2545.     goVertLine, goColSizing, goColMoving, goTabs, goEditing];
  2546.   FColumns := CreateColumns;
  2547.   inherited RowCount := 2;
  2548.   inherited ColCount := 2;
  2549.   FDataLink := TIvGridDataLink.Create(Self);
  2550.   Color := clWindow;
  2551.   ParentColor := False;
  2552.   FTitleFont := TFont.Create;
  2553.   FTitleFont.OnChange := TitleFontChanged;
  2554.   FSaveCellExtents := False;
  2555.   FUserChange := True;
  2556.   FDefaultDrawing := True;
  2557.   FUpdatingEditor := False;
  2558.   FBookmarks := TIvBookmarkList.Create(Self);
  2559.   HideEditor;
  2560. end;
  2561.  
  2562. destructor TIvCustomDBGrid.Destroy;
  2563. begin
  2564.   FColumns.Free;
  2565.   FColumns := nil;
  2566.   FDataLink.Free;
  2567.   FDataLink := nil;
  2568.   FIndicators.Free;
  2569.   FTitleFont.Free;
  2570.   FTitleFont := nil;
  2571.   FBookmarks.Free;
  2572.   FBookmarks := nil;
  2573.   inherited Destroy;
  2574.   ReleaseBitmap;
  2575. end;
  2576.  
  2577. function TIvCustomDBGrid.AcquireFocus: Boolean;
  2578. begin
  2579.   Result := True;
  2580.   if FAcquireFocus and CanFocus and not (csDesigning in ComponentState) then
  2581.   begin
  2582.     SetFocus;
  2583.     Result := Focused or (InplaceEditor <> nil) and InplaceEditor.Focused;
  2584.   end;
  2585. end;
  2586.  
  2587. function TIvCustomDBGrid.RawToDataColumn(ACol: Integer): Integer;
  2588. begin
  2589.   Result := ACol - FIndicatorOffset;
  2590. end;
  2591.  
  2592. function TIvCustomDBGrid.DataToRawColumn(ACol: Integer): Integer;
  2593. begin
  2594.   Result := ACol + FIndicatorOffset;
  2595. end;
  2596.  
  2597. function TIvCustomDBGrid.AcquireLayoutLock: Boolean;
  2598. begin
  2599.   Result := (FUpdateLock = 0) and (FLayoutLock = 0);
  2600.   if Result then BeginLayout;
  2601. end;
  2602.  
  2603. procedure TIvCustomDBGrid.BeginLayout;
  2604. begin
  2605.   BeginUpdate;
  2606.   if FLayoutLock = 0 then Columns.BeginUpdate;
  2607.   Inc(FLayoutLock);
  2608. end;
  2609.  
  2610. procedure TIvCustomDBGrid.BeginUpdate;
  2611. begin
  2612.   Inc(FUpdateLock);
  2613. end;
  2614.  
  2615. procedure TIvCustomDBGrid.CancelLayout;
  2616. begin
  2617.   if FLayoutLock > 0 then
  2618.   begin
  2619.     if FLayoutLock = 1 then
  2620.       Columns.EndUpdate;
  2621.     Dec(FLayoutLock);
  2622.     EndUpdate;
  2623.   end;
  2624. end;
  2625.  
  2626. function TIvCustomDBGrid.CanEditAcceptKey(Key: Char): Boolean;
  2627. begin
  2628.   with Columns[SelectedIndex] do
  2629.     Result := FDatalink.Active and Assigned(Field) and Field.IsValidChar(Key);
  2630. end;
  2631.  
  2632. function TIvCustomDBGrid.CanEditModify: Boolean;
  2633. begin
  2634.   Result := False;
  2635.   if not ReadOnly and FDatalink.Active and not FDatalink.Readonly then
  2636.   with Columns[SelectedIndex] do
  2637.     if (not ReadOnly) and Assigned(Field) and Field.CanModify
  2638.       and (not
  2639. {$IFDEF IVWIDE}
  2640.       Field.IsBlob
  2641. {$ELSE}
  2642.       (Field is TBlobField)
  2643. {$ENDIF}
  2644.       or Assigned(Field.OnSetText)) then
  2645.     begin
  2646.       FDatalink.Edit;
  2647.       Result := FDatalink.Editing;
  2648.       if Result then FDatalink.Modified;
  2649.     end;
  2650. end;
  2651.  
  2652. function TIvCustomDBGrid.CanEditShow: Boolean;
  2653. begin
  2654.   Result := (LayoutLock = 0) and inherited CanEditShow;
  2655. end;
  2656.  
  2657. procedure TIvCustomDBGrid.CellClick(Column: TIvColumn);
  2658. begin
  2659.   if Assigned(FOnCellClick) then
  2660.     FOnCellClick(Column);
  2661. end;
  2662.  
  2663. procedure TIvCustomDBGrid.ColEnter;
  2664. begin
  2665. {$IFDEF IVIME}
  2666.   UpdateIme;
  2667. {$ENDIF}
  2668.   if Assigned(FOnColEnter) then
  2669.     FOnColEnter(Self);
  2670. end;
  2671.  
  2672. procedure TIvCustomDBGrid.ColExit;
  2673. begin
  2674.   if Assigned(FOnColExit) then
  2675.     FOnColExit(Self);
  2676. end;
  2677.  
  2678. procedure TIvCustomDBGrid.ColumnMoved(FromIndex, ToIndex: Longint);
  2679. begin
  2680.   FromIndex := RawToDataColumn(FromIndex);
  2681.   ToIndex := RawToDataColumn(ToIndex);
  2682.   Columns[FromIndex].Index := ToIndex;
  2683.   if Assigned(FOnColumnMoved) then
  2684.     FOnColumnMoved(Self, FromIndex, ToIndex);
  2685. end;
  2686.  
  2687. procedure TIvCustomDBGrid.ColWidthsChanged;
  2688. var
  2689.   I: Integer;
  2690. begin
  2691.   inherited ColWidthsChanged;
  2692.   if (FDatalink.Active or (FColumns.State = csCustomized)) and
  2693.     AcquireLayoutLock then
  2694.   try
  2695.     for I := FIndicatorOffset to ColCount - 1 do
  2696.       FColumns[I - FIndicatorOffset].Width := ColWidths[I];
  2697.   finally
  2698.     EndLayout;
  2699.   end;
  2700. end;
  2701.  
  2702. function TIvCustomDBGrid.CreateColumns: TIvDBGridColumns;
  2703. begin
  2704.   Result := TIvDBGridColumns.Create(Self, TIvColumn);
  2705. end;
  2706.  
  2707. function TIvCustomDBGrid.CreateEditor: TIvInplaceEdit;
  2708. begin
  2709.   Result := TIvDBGridInplaceEdit.Create(Self);
  2710. end;
  2711.  
  2712. procedure TIvCustomDBGrid.CreateWnd;
  2713. begin
  2714.   BeginUpdate;   { prevent updates in WMSize message that follows WMCreate }
  2715.   try
  2716.     inherited CreateWnd;
  2717.   finally
  2718.     EndUpdate;
  2719.   end;
  2720.   UpdateRowCount;
  2721.   UpdateActive;
  2722.   UpdateScrollBar;
  2723. {$IFDEF IVIME}
  2724.   FOriginalImeName := ImeName;
  2725.   FOriginalImeMode := ImeMode;
  2726. {$ENDIF}
  2727. end;
  2728.  
  2729. procedure TIvCustomDBGrid.DataChanged;
  2730. begin
  2731.   if not HandleAllocated then Exit;
  2732.   UpdateRowCount;
  2733.   UpdateScrollBar;
  2734.   UpdateActive;
  2735.   InvalidateEditor;
  2736.   ValidateRect(Handle, nil);
  2737.   Invalidate;
  2738. end;
  2739.  
  2740. procedure TIvCustomDBGrid.DefaultHandler(var Msg);
  2741. var
  2742.   P: TPopupMenu;
  2743.   Cell: TIvGridCoord;
  2744. begin
  2745.   inherited DefaultHandler(Msg);
  2746.   if TMessage(Msg).Msg = wm_RButtonUp then
  2747.     with TWMRButtonUp(Msg) do
  2748.     begin
  2749.       Cell := MouseCoord(XPos, YPos);
  2750.       if (Cell.X < FIndicatorOffset) or (Cell.Y < 0) then Exit;
  2751.       P := Columns[RawToDataColumn(Cell.X)].PopupMenu;
  2752.       if (P <> nil) and P.AutoPopup then
  2753.       begin
  2754.         SendCancelMode(nil);
  2755.         P.PopupComponent := Self;
  2756.         with ClientToScreen(SmallPointToPoint(Pos)) do
  2757.           P.Popup(X, Y);
  2758.         Result := 1;
  2759.       end;
  2760.     end;
  2761. end;
  2762.  
  2763. procedure TIvCustomDBGrid.DeferLayout;
  2764. var
  2765.   M: TMsg;
  2766. begin
  2767.   if HandleAllocated and
  2768.     not PeekMessage(M, Handle, cm_DeferLayout, cm_DeferLayout, pm_NoRemove) then
  2769.     PostMessage(Handle, cm_DeferLayout, 0, 0);
  2770.   CancelLayout;
  2771. end;
  2772.  
  2773. procedure TIvCustomDBGrid.DefineFieldMap;
  2774. var
  2775.   I: Integer;
  2776. begin
  2777.   if FColumns.State = csCustomized then
  2778.   begin   { Build the column/field map from the column attributes }
  2779.     DataLink.SparseMap := True;
  2780.     for I := 0 to FColumns.Count-1 do
  2781.       FDataLink.AddMapping(FColumns[I].FieldName);
  2782.   end
  2783.   else   { Build the column/field map from the field list order }
  2784.   begin
  2785.     FDataLink.SparseMap := False;
  2786.     with Datalink.Dataset do
  2787.       for I := 0 to FieldCount - 1 do
  2788.         with Fields[I] do if Visible then Datalink.AddMapping(FieldName);
  2789.   end;
  2790. end;
  2791.  
  2792. procedure TIvCustomDBGrid.DefaultDrawDataCell(
  2793.   const Rect: TRect;
  2794.   Field: TField;
  2795.   State: TIvGridDrawState);
  2796. var
  2797.   Alignment: TAlignment;
  2798.   Value: string;
  2799. begin
  2800.   Alignment := taLeftJustify;
  2801.   Value := '';
  2802.   if Assigned(Field) then
  2803.   begin
  2804.     Alignment := Field.Alignment;
  2805.     Value := Field.DisplayText;
  2806.   end;
  2807.     WriteText(Canvas, Rect, 2, 2, Value, Alignment, False);
  2808. end;
  2809.  
  2810. procedure TIvCustomDBGrid.DefaultDrawColumnCell(
  2811.   const Rect: TRect;
  2812.   DataCol: Integer;
  2813.   Column: TIvColumn;
  2814.   State: TIvGridDrawState);
  2815. var
  2816.   Value: string;
  2817. begin
  2818.   Value := '';
  2819.   if Assigned(Column.Field) then
  2820.     Value := Column.Field.DisplayText;
  2821.     WriteText(Canvas, Rect, 2, 2, Value, Column.Alignment, False);
  2822. end;
  2823.  
  2824. {$IFDEF IVWIDE}
  2825. procedure TIvCustomDBGrid.ReadColumns(Reader: TReader);
  2826. begin
  2827.   Columns.Clear;
  2828.   Reader.ReadValue;
  2829.   Reader.ReadCollection(Columns);
  2830. end;
  2831.  
  2832. procedure TIvCustomDBGrid.WriteColumns(Writer: TWriter);
  2833. begin
  2834.   Writer.WriteCollection(Columns);
  2835. end;
  2836.  
  2837. procedure TIvCustomDBGrid.DefineProperties(Filer: TFiler);
  2838. begin
  2839.   Filer.DefineProperty('Columns', ReadColumns, WriteColumns,
  2840.     ((Columns.State = csCustomized) and (Filer.Ancestor = nil)) or
  2841.     ((Filer.Ancestor <> nil) and
  2842.      ((Columns.State <> TIvCustomDBGrid(Filer.Ancestor).Columns.State) or
  2843.       (not CollectionsEqual(Columns, TIvCustomDBGrid(Filer.Ancestor).Columns)) )));
  2844. end;
  2845. {$ENDIF}
  2846.  
  2847. procedure TIvCustomDBGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TIvGridDrawState);
  2848.  
  2849.   function RowIsMultiSelected: Boolean;
  2850.   var
  2851.     Index: Integer;
  2852.   begin
  2853.     Result := (dgMultiSelect in Options) and Datalink.Active and
  2854.       FBookmarks.Find(Datalink.Datasource.Dataset.Bookmark, Index);
  2855.   end;
  2856.  
  2857. var
  2858.   OldActive: Integer;
  2859.   Indicator: Integer;
  2860.   Highlight: Boolean;
  2861.   Value: string;
  2862.   DrawColumn: TIvColumn;
  2863.   FrameOffs: Byte;
  2864.   MultiSelected: Boolean;
  2865. begin
  2866.   if csLoading in ComponentState then
  2867.   begin
  2868.     Canvas.Brush.Color := Color;
  2869.     Canvas.FillRect(ARect);
  2870.     Exit;
  2871.   end;
  2872.  
  2873.   Dec(ARow, FTitleOffset);
  2874.   Dec(ACol, FIndicatorOffset);
  2875.  
  2876.   if (gdFixed in AState) and ([dgRowLines, dgColLines] * Options =
  2877.     [dgRowLines, dgColLines]) then
  2878.   begin
  2879.     InflateRect(ARect, -1, -1);
  2880.     FrameOffs := 1;
  2881.   end
  2882.   else
  2883.     FrameOffs := 2;
  2884.  
  2885.   if (gdFixed in AState) and (ACol < 0) then
  2886.   begin
  2887.     Canvas.Brush.Color := FixedColor;
  2888.     Canvas.FillRect(ARect);
  2889.     if Assigned(DataLink) and DataLink.Active  then
  2890.     begin
  2891.       MultiSelected := False;
  2892.       if ARow >= 0 then
  2893.       begin
  2894.         OldActive := FDataLink.ActiveRecord;
  2895.         try
  2896.           FDatalink.ActiveRecord := ARow;
  2897.           MultiSelected := RowIsMultiselected;
  2898.         finally
  2899.           FDatalink.ActiveRecord := OldActive;
  2900.         end;
  2901.       end;
  2902.       if (ARow = FDataLink.ActiveRecord) or MultiSelected then
  2903.       begin
  2904.         Indicator := 0;
  2905.         if FDataLink.DataSet <> nil then
  2906.           case FDataLink.DataSet.State of
  2907.             dsEdit: Indicator := 1;
  2908.             dsInsert: Indicator := 2;
  2909.             dsBrowse:
  2910.               if MultiSelected then
  2911.                 if (ARow <> FDatalink.ActiveRecord) then
  2912.                   Indicator := 3
  2913.                 else
  2914.                   Indicator := 4;  // multiselected and current row
  2915.           end;
  2916.         FIndicators.BkColor := FixedColor;
  2917.         FIndicators.Draw(Canvas, ARect.Right - FIndicators.Width - FrameOffs,
  2918.           (ARect.Top + ARect.Bottom - FIndicators.Height) shr 1, Indicator);
  2919.         if ARow = FDatalink.ActiveRecord then
  2920.           FSelRow := ARow + FTitleOffset;
  2921.       end;
  2922.     end;
  2923.   end
  2924.   else with Canvas do
  2925.   begin
  2926.     DrawColumn := Columns[ACol];
  2927.     if gdFixed in AState then
  2928.     begin
  2929.       Font := DrawColumn.Title.Font;
  2930.       Brush.Color := DrawColumn.Title.Color;
  2931.     end
  2932.     else
  2933.     begin
  2934.       Font := DrawColumn.Font;
  2935.       Brush.Color := DrawColumn.Color;
  2936.     end;
  2937.     if ARow < 0 then with DrawColumn.Title do
  2938.     begin
  2939.         WriteText(Canvas, ARect, FrameOffs, FrameOffs, Caption, Alignment, False)
  2940.     end
  2941.     else if (FDataLink = nil) or not FDataLink.Active then
  2942.       FillRect(ARect)
  2943.     else
  2944.     begin
  2945.       Value := '';
  2946.       OldActive := FDataLink.ActiveRecord;
  2947.       try
  2948.         FDataLink.ActiveRecord := ARow;
  2949.         if Assigned(DrawColumn.Field) then
  2950.           Value := DrawColumn.Field.DisplayText;
  2951.         Highlight := HighlightCell(ACol, ARow, Value, AState);
  2952.         if Highlight then
  2953.         begin
  2954.           Brush.Color := clHighlight;
  2955.           Font.Color := clHighlightText;
  2956.         end;
  2957.         if FDefaultDrawing then
  2958.         begin
  2959.             WriteText(Canvas, ARect, 2, 2, Value, DrawColumn.Alignment, False);
  2960.         end;
  2961.         if Columns.State = csDefault then
  2962.           DrawDataCell(ARect, DrawColumn.Field, AState);
  2963.         DrawColumnCell(ARect, ACol, DrawColumn, AState);
  2964.       finally
  2965.         FDataLink.ActiveRecord := OldActive;
  2966.       end;
  2967.       if FDefaultDrawing and (gdSelected in AState)
  2968.         and ((dgAlwaysShowSelection in Options) or Focused)
  2969.         and not (csDesigning in ComponentState)
  2970.         and not (dgRowSelect in Options)
  2971.         and (UpdateLock = 0)
  2972.         and (ValidParentForm(Self).ActiveControl = Self) then
  2973.         Windows.DrawFocusRect(Handle, ARect);
  2974.     end;
  2975.   end;
  2976.   if (gdFixed in AState) and ([dgRowLines, dgColLines] * Options =
  2977.     [dgRowLines, dgColLines]) then
  2978.   begin
  2979.     InflateRect(ARect, 1, 1);
  2980.     DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
  2981.     DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_TOPLEFT);
  2982.   end;
  2983. end;
  2984.  
  2985. procedure TIvCustomDBGrid.DrawDataCell(const Rect: TRect; Field: TField;
  2986.   State: TIvGridDrawState);
  2987. begin
  2988.   if Assigned(FOnDrawDataCell) then FOnDrawDataCell(Self, Rect, Field, State);
  2989. end;
  2990.  
  2991. procedure TIvCustomDBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer;
  2992.   Column: TIvColumn; State: TIvGridDrawState);
  2993. begin
  2994.   if Assigned(OnDrawColumnCell) then
  2995.     OnDrawColumnCell(Self, Rect, DataCol, Column, State);
  2996. end;
  2997.  
  2998. procedure TIvCustomDBGrid.EditButtonClick;
  2999. begin
  3000.   if Assigned(FOnEditButtonClick) then FOnEditButtonClick(Self);
  3001. end;
  3002.  
  3003. procedure TIvCustomDBGrid.EditingChanged;
  3004. begin
  3005.   if dgIndicator in Options then InvalidateCell(0, FSelRow);
  3006. end;
  3007.  
  3008. procedure TIvCustomDBGrid.EndLayout;
  3009. begin
  3010.   if FLayoutLock > 0 then
  3011.   begin
  3012.     try
  3013.       try
  3014.         if FLayoutLock = 1 then
  3015.           InternalLayout;
  3016.       finally
  3017.         if FLayoutLock = 1 then
  3018.           FColumns.EndUpdate;
  3019.       end;
  3020.     finally
  3021.       Dec(FLayoutLock);
  3022.       EndUpdate;
  3023.     end;
  3024.   end;
  3025. end;
  3026.  
  3027. procedure TIvCustomDBGrid.EndUpdate;
  3028. begin
  3029.   if FUpdateLock > 0 then
  3030.     Dec(FUpdateLock);
  3031. end;
  3032.  
  3033. function TIvCustomDBGrid.GetColField(DataCol: Integer): TField;
  3034. begin
  3035.   Result := nil;
  3036.   if (DataCol >= 0) and FDatalink.Active and (DataCol < Columns.Count) then
  3037.     Result := Columns[DataCol].Field;
  3038. end;
  3039.  
  3040. function TIvCustomDBGrid.GetDataSource: TDataSource;
  3041. begin
  3042.   Result := FDataLink.DataSource;
  3043. end;
  3044.  
  3045. function TIvCustomDBGrid.GetEditLimit: Integer;
  3046. begin
  3047.   Result := 0;
  3048.   if Assigned(SelectedField) and (SelectedField.DataType = ftString) then
  3049.     Result := SelectedField.Size;
  3050. end;
  3051.  
  3052. function TIvCustomDBGrid.GetEditMask(ACol, ARow: Longint): string;
  3053. begin
  3054.   Result := '';
  3055.   if FDatalink.Active then
  3056.   with Columns[RawToDataColumn(ACol)] do
  3057.     if Assigned(Field) then
  3058.       Result := Field.EditMask;
  3059. end;
  3060.  
  3061. function TIvCustomDBGrid.GetEditText(ACol, ARow: Longint): string;
  3062. begin
  3063.   Result := '';
  3064.   if FDatalink.Active then
  3065.   with Columns[RawToDataColumn(ACol)] do
  3066.     if Assigned(Field) then
  3067.       Result := Field.Text;
  3068.   FEditText := Result;
  3069. end;
  3070.  
  3071. function TIvCustomDBGrid.GetFieldCount: Integer;
  3072. begin
  3073.   Result := FDatalink.FieldCount;
  3074. end;
  3075.  
  3076. function TIvCustomDBGrid.GetFields(FieldIndex: Integer): TField;
  3077. begin
  3078.   Result := FDatalink.Fields[FieldIndex];
  3079. end;
  3080.  
  3081. function TIvCustomDBGrid.GetFieldValue(ACol: Integer): string;
  3082. var
  3083.   Field: TField;
  3084. begin
  3085.   Result := '';
  3086.   Field := GetColField(ACol);
  3087.   if Field <> nil then Result := Field.DisplayText;
  3088. end;
  3089.  
  3090. function TIvCustomDBGrid.GetSelectedField: TField;
  3091. var
  3092.   Index: Integer;
  3093. begin
  3094.   Index := SelectedIndex;
  3095.   if Index <> -1 then
  3096.     Result := Columns[Index].Field
  3097.   else
  3098.     Result := nil;
  3099. end;
  3100.  
  3101. function TIvCustomDBGrid.GetSelectedIndex: Integer;
  3102. begin
  3103.   Result := RawToDataColumn(Col);
  3104. end;
  3105.  
  3106. function TIvCustomDBGrid.HighlightCell(DataCol, DataRow: Integer;
  3107.   const Value: string; AState: TIvGridDrawState): Boolean;
  3108. var
  3109.   Index: Integer;
  3110. begin
  3111.   Result := False;
  3112.   if (dgMultiSelect in Options) and Datalink.Active then
  3113.     Result := FBookmarks.Find(Datalink.Datasource.Dataset.Bookmark, Index);
  3114.   if not Result then
  3115.     Result := (gdSelected in AState)
  3116.       and ((dgAlwaysShowSelection in Options) or Focused)
  3117.         { updatelock eliminates flicker when tabbing between rows }
  3118.       and ((UpdateLock = 0) or (dgRowSelect in Options));
  3119. end;
  3120.  
  3121. procedure TIvCustomDBGrid.KeyDown(var Key: Word; Shift: TShiftState);
  3122. var
  3123.   KeyDownEvent: TKeyEvent;
  3124.  
  3125.   procedure ClearSelection;
  3126.   begin
  3127.     if (dgMultiSelect in Options) then
  3128.     begin
  3129.       FBookmarks.Clear;
  3130.       FSelecting := False;
  3131.     end;
  3132.   end;
  3133.  
  3134.   procedure DoSelection(Select: Boolean; Direction: Integer);
  3135.   var
  3136.     AddAfter: Boolean;
  3137.   begin
  3138.     AddAfter := False;
  3139.     BeginUpdate;
  3140.     try
  3141.       if (dgMultiSelect in Options) and FDatalink.Active then
  3142.         if Select and (ssShift in Shift) then
  3143.         begin
  3144.           if not FSelecting then
  3145.           begin
  3146.             FSelectionAnchor := FBookmarks.CurrentRow;
  3147.             FBookmarks.CurrentRowSelected := True;
  3148.             FSelecting := True;
  3149.             AddAfter := True;
  3150.           end
  3151.           else
  3152.           with FBookmarks do
  3153.           begin
  3154.             AddAfter := Compare(CurrentRow, FSelectionAnchor) <> -Direction;
  3155.             if not AddAfter then
  3156.               CurrentRowSelected := False;
  3157.           end
  3158.         end
  3159.         else
  3160.           ClearSelection;
  3161.       FDatalink.Dataset.MoveBy(Direction);
  3162.       if AddAfter then FBookmarks.CurrentRowSelected := True;
  3163.     finally
  3164.       EndUpdate;
  3165.     end;
  3166.   end;
  3167.  
  3168.   procedure NextRow(Select: Boolean);
  3169.   begin
  3170.     with FDatalink.Dataset do
  3171.     begin
  3172.       if (State = dsInsert) and not Modified and not FDatalink.FModified then
  3173.         if EOF then Exit else Cancel
  3174.       else
  3175.         DoSelection(Select, 1);
  3176.       if EOF and CanModify and (not ReadOnly) and (dgEditing in Options) then
  3177.         Append;
  3178.     end;
  3179.   end;
  3180.  
  3181.   procedure PriorRow(Select: Boolean);
  3182.   begin
  3183.     with FDatalink.Dataset do
  3184.       if (State = dsInsert) and not Modified and EOF and
  3185.         not FDatalink.FModified then
  3186.         Cancel
  3187.       else
  3188.         DoSelection(Select, -1);
  3189.   end;
  3190.  
  3191.   procedure Tab(GoForward: Boolean);
  3192.   var
  3193.     ACol, Original: Integer;
  3194.   begin
  3195.     ACol := Col;
  3196.     Original := ACol;
  3197.     BeginUpdate;    { Prevent highlight flicker on tab to next/prior row }
  3198.     try
  3199.       while True do
  3200.       begin
  3201.         if GoForward then
  3202.           Inc(ACol) else
  3203.           Dec(ACol);
  3204.         if ACol >= ColCount then
  3205.         begin
  3206.           NextRow(False);
  3207.           ACol := FIndicatorOffset;
  3208.         end
  3209.         else if ACol < FIndicatorOffset then
  3210.         begin
  3211.           PriorRow(False);
  3212.           ACol := ColCount;
  3213.         end;
  3214.         if ACol = Original then Exit;
  3215.         if TabStops[ACol] then
  3216.         begin
  3217.           MoveCol(ACol);
  3218.           Exit;
  3219.         end;
  3220.       end;
  3221.     finally
  3222.       EndUpdate;
  3223.     end;
  3224.   end;
  3225.  
  3226.   function DeletePrompt: Boolean;
  3227.   var
  3228.     Msg: String;
  3229.   begin
  3230.     if (FBookmarks.Count > 1) then
  3231.       Msg := 'Delete all selected records?'
  3232.     else
  3233.       Msg := 'Delete record?';
  3234.     Result :=
  3235.       not (dgConfirmDelete in Options) or
  3236.       (MessageDlg(Msg, mtConfirmation, mbOKCancel, 0) <> idCancel);
  3237.   end;
  3238.  
  3239. const
  3240.   RowMovementKeys = [VK_UP, VK_PRIOR, VK_DOWN, VK_NEXT, VK_HOME, VK_END];
  3241.  
  3242. begin
  3243.   KeyDownEvent := OnKeyDown;
  3244.   if Assigned(KeyDownEvent) then KeyDownEvent(Self, Key, Shift);
  3245.   if not FDatalink.Active or not CanGridAcceptKey(Key, Shift) then Exit;
  3246.   with FDatalink.DataSet do
  3247.     if ssCtrl in Shift then
  3248.     begin
  3249.       if (Key in RowMovementKeys) then ClearSelection;
  3250.       case Key of
  3251.         VK_UP, VK_PRIOR: MoveBy(-FDatalink.ActiveRecord);
  3252.         VK_DOWN, VK_NEXT: MoveBy(FDatalink.BufferCount - FDatalink.ActiveRecord - 1);
  3253.         VK_LEFT: MoveCol(FIndicatorOffset);
  3254.         VK_RIGHT: MoveCol(ColCount - 1);
  3255.         VK_HOME: First;
  3256.         VK_END: Last;
  3257.         VK_DELETE:
  3258.           if (not ReadOnly) and
  3259. {$IFDEF IVWIDE}
  3260.             not IsEmpty and
  3261. {$ENDIF}
  3262.             CanModify and DeletePrompt then
  3263.           begin
  3264.             if FBookmarks.Count > 0 then
  3265.               FBookmarks.Delete
  3266.             else
  3267.               Delete;
  3268.           end;
  3269.       end
  3270.     end
  3271.     else
  3272.       case Key of
  3273.         VK_UP: PriorRow(True);
  3274.         VK_DOWN: NextRow(True);
  3275.         VK_LEFT:
  3276.           begin
  3277.             if dgRowSelect in Options then
  3278.               PriorRow(False)
  3279.             else
  3280.               MoveCol(Col - 1);
  3281.           end;
  3282.  
  3283.         VK_RIGHT:
  3284.           begin
  3285.             if dgRowSelect in Options then
  3286.               NextRow(False)
  3287.             else
  3288.               MoveCol(Col + 1);
  3289.           end;
  3290.  
  3291.         VK_HOME:
  3292.           if (ColCount = FIndicatorOffset+1)
  3293.             or (dgRowSelect in Options) then
  3294.           begin
  3295.             ClearSelection;
  3296.             First;
  3297.           end
  3298.           else
  3299.             MoveCol(FIndicatorOffset);
  3300.         VK_END:
  3301.           if (ColCount = FIndicatorOffset+1)
  3302.             or (dgRowSelect in Options) then
  3303.           begin
  3304.             ClearSelection;
  3305.             Last;
  3306.           end
  3307.           else
  3308.             MoveCol(ColCount - 1);
  3309.         VK_NEXT:
  3310.           begin
  3311.             ClearSelection;
  3312.             MoveBy(VisibleRowCount);
  3313.           end;
  3314.         VK_PRIOR:
  3315.           begin
  3316.             ClearSelection;
  3317.             MoveBy(-VisibleRowCount);
  3318.           end;
  3319.         VK_INSERT:
  3320.           if CanModify and (not ReadOnly) and (dgEditing in Options) then
  3321.           begin
  3322.             ClearSelection;
  3323.             Insert;
  3324.           end;
  3325.         VK_TAB: if not (ssAlt in Shift) then Tab(not (ssShift in Shift));
  3326.         VK_ESCAPE:
  3327.           begin
  3328.             FDatalink.Reset;
  3329.             ClearSelection;
  3330.             if not (dgAlwaysShowEditor in Options) then HideEditor;
  3331.           end;
  3332.         VK_F2: EditorMode := True;
  3333.       end;
  3334. end;
  3335.  
  3336. procedure TIvCustomDBGrid.KeyPress(var Key: Char);
  3337. begin
  3338.   if not (dgAlwaysShowEditor in Options) and (Key = #13) then
  3339.     FDatalink.UpdateData;
  3340.   inherited KeyPress(Key);
  3341. end;
  3342.  
  3343. { InternalLayout is called with layout locks and column locks in effect }
  3344. procedure TIvCustomDBGrid.InternalLayout;
  3345. var
  3346.   I, J, K: Integer;
  3347.   Fld: TField;
  3348.   Column: TIvColumn;
  3349.   SeenPassthrough: Boolean;
  3350.   RestoreCanvas: Boolean;
  3351.  
  3352.   function FieldIsMapped(F: TField): Boolean;
  3353.   var
  3354.     X: Integer;
  3355.   begin
  3356.     Result := False;
  3357.     if F = nil then Exit;
  3358.     for X := 0 to FDatalink.FieldCount-1 do
  3359.       if FDatalink.Fields[X] = F then
  3360.       begin
  3361.         Result := True;
  3362.         Exit;
  3363.       end;
  3364.   end;
  3365.  
  3366. begin
  3367.   if (csLoading in ComponentState) then Exit;
  3368.  
  3369.   if HandleAllocated then KillMessage(Handle, cm_DeferLayout);
  3370.  
  3371.   { Check for Columns.State flip-flop }
  3372.   SeenPassthrough := False;
  3373.   for I := 0 to FColumns.Count-1 do
  3374.   begin
  3375.     if (FColumns[I] is TPassthroughColumn) then
  3376.       SeenPassthrough := True
  3377.     else
  3378.       if SeenPassthrough then
  3379.       begin   { We have both custom and passthrough columns. Kill the latter }
  3380.         for J := FColumns.Count-1 downto 0 do
  3381.         begin
  3382.           Column := FColumns[J];
  3383.           if Column is TPassthroughColumn then
  3384.             Column.Free;
  3385.         end;
  3386.         Break;
  3387.       end;
  3388.   end;
  3389.  
  3390.   FIndicatorOffset := 0;
  3391.   if dgIndicator in Options then
  3392.     Inc(FIndicatorOffset);
  3393.   FDatalink.ClearMapping;
  3394.   if FDatalink.Active then DefineFieldMap;
  3395.   if FColumns.State = csDefault then
  3396.   begin
  3397.      { Destroy columns whose fields have been destroyed or are no longer
  3398.        in field map }
  3399.     if (not FDataLink.Active) and (FDatalink.DefaultFields) then
  3400.       FColumns.Clear
  3401.     else
  3402.       for J := FColumns.Count-1 downto 0 do
  3403.         with FColumns[J] do
  3404.         if not Assigned(Field)
  3405.           or not FieldIsMapped(Field) then Free;
  3406.     I := FDataLink.FieldCount;
  3407.     if (I = 0) and (FColumns.Count = 0) then Inc(I);
  3408.     for J := 0 to I-1 do
  3409.     begin
  3410.       Fld := FDatalink.Fields[J];
  3411.       if Assigned(Fld) then
  3412.       begin
  3413.         K := J;
  3414.          { Pointer compare is valid here because the grid sets matching
  3415.            column.field properties to nil in response to field object
  3416.            free notifications.  Closing a dataset that has only default
  3417.            field objects will destroy all the fields and set associated
  3418.            column.field props to nil. }
  3419.         while (K < FColumns.Count) and (FColumns[K].Field <> Fld) do
  3420.           Inc(K);
  3421.         if K < FColumns.Count then
  3422.           Column := FColumns[K]
  3423.         else
  3424.         begin
  3425.           Column := TPassthroughColumn.Create(FColumns);
  3426.           Column.Field := Fld;
  3427.         end;
  3428.       end
  3429.       else
  3430.         Column := TPassthroughColumn.Create(FColumns);
  3431.       Column.Index := J;
  3432.     end;
  3433.   end
  3434.   else
  3435.   begin
  3436.     { Force columns to reaquire fields (in case dataset has changed) }
  3437.     for I := 0 to FColumns.Count-1 do
  3438.       FColumns[I].Field := nil;
  3439.   end;
  3440.   ColCount := FColumns.Count + FIndicatorOffset;
  3441.   inherited FixedCols := FIndicatorOffset;
  3442.   FTitleOffset := 0;
  3443.   if dgTitles in Options then FTitleOffset := 1;
  3444.   RestoreCanvas := not HandleAllocated;
  3445.   if RestoreCanvas then
  3446.     Canvas.Handle := GetDC(0);
  3447.   try
  3448.     Canvas.Font := Font;
  3449.     K := Canvas.TextHeight('Wg') + 3;
  3450.     if dgRowLines in Options then
  3451.       Inc(K, GridLineWidth);
  3452.     DefaultRowHeight := K;
  3453.     if dgTitles in Options then
  3454.     begin
  3455.       K := 0;
  3456.       for I := 0 to FColumns.Count-1 do
  3457.       begin
  3458.         Canvas.Font := FColumns[I].Title.Font;
  3459.         J := Canvas.TextHeight('Wg') + 4;
  3460.         if J > K then K := J;
  3461.       end;
  3462.       if K = 0 then
  3463.       begin
  3464.         Canvas.Font := FTitleFont;
  3465.         K := Canvas.TextHeight('Wg') + 4;
  3466.       end;
  3467.       RowHeights[0] := K;
  3468.     end;
  3469.   finally
  3470.     if RestoreCanvas then
  3471.     begin
  3472.       ReleaseDC(0,Canvas.Handle);
  3473.       Canvas.Handle := 0;
  3474.     end;
  3475.   end;
  3476.   UpdateRowCount;
  3477.   SeTIvColumnAttributes;
  3478.   UpdateActive;
  3479.   Invalidate;
  3480. end;
  3481.  
  3482. procedure TIvCustomDBGrid.LayoutChanged;
  3483. begin
  3484.   if AcquireLayoutLock then
  3485.     EndLayout;
  3486. end;
  3487.  
  3488. procedure TIvCustomDBGrid.LinkActive(Value: Boolean);
  3489. begin
  3490.   if not Value then HideEditor;
  3491.   FBookmarks.LinkActive(Value);
  3492.   LayoutChanged;
  3493.   UpdateScrollBar;
  3494.   if Value and (dgAlwaysShowEditor in Options) then ShowEditor;
  3495. end;
  3496.  
  3497. procedure TIvCustomDBGrid.Loaded;
  3498. begin
  3499.   inherited Loaded;
  3500.   if FColumns.Count > 0 then
  3501.     ColCount := FColumns.Count;
  3502.   LayoutChanged;
  3503. end;
  3504.  
  3505. procedure TIvCustomDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState;
  3506.   X, Y: Integer);
  3507. var
  3508.   Cell: TIvGridCoord;
  3509.   OldCol,OldRow: Integer;
  3510. begin
  3511.   if not AcquireFocus then Exit;
  3512.   if (ssDouble in Shift) and (Button = mbLeft) then
  3513.   begin
  3514.     DblClick;
  3515.     Exit;
  3516.   end;
  3517.   if Sizing(X, Y) then
  3518.   begin
  3519.     FDatalink.UpdateData;
  3520.     inherited MouseDown(Button, Shift, X, Y)
  3521.   end
  3522.   else
  3523.   begin
  3524.     Cell := MouseCoord(X, Y);
  3525.     if ((csDesigning in ComponentState) or (dgColumnResize in Options)) and
  3526.       (Cell.Y < FTitleOffset) then
  3527.     begin
  3528.       FDataLink.UpdateData;
  3529.       inherited MouseDown(Button, Shift, X, Y)
  3530.     end
  3531.     else
  3532.       if FDatalink.Active then
  3533.         with Cell do
  3534.         begin
  3535.           BeginUpdate;   { eliminates highlight flicker when selection moves }
  3536.           try
  3537.             HideEditor;
  3538.             OldCol := Col;
  3539.             OldRow := Row;
  3540.             if (Y >= FTitleOffset) and (Y - Row <> 0) then
  3541.               FDatalink.Dataset.MoveBy(Y - Row);
  3542.             if X >= FIndicatorOffset then
  3543.               MoveCol(X);
  3544.             if (dgMultiSelect in Options) and FDatalink.Active then
  3545.               with FBookmarks do
  3546.               begin
  3547.                 FSelecting := False;
  3548.                 if ssCtrl in Shift then
  3549.                   CurrentRowSelected := not CurrentRowSelected
  3550.                 else
  3551.                 begin
  3552.                   Clear;
  3553.                   CurrentRowSelected := True;
  3554.                 end;
  3555.               end;
  3556.             if (Button = mbLeft) and
  3557.               (((X = OldCol) and (Y = OldRow)) or (dgAlwaysShowEditor in Options)) then
  3558.               ShowEditor         { put grid in edit mode }
  3559.             else
  3560.               InvalidateEditor;  { draw editor, if needed }
  3561.           finally
  3562.             EndUpdate;
  3563.           end;
  3564.         end;
  3565.   end;
  3566. end;
  3567.  
  3568. procedure TIvCustomDBGrid.MouseUp(Button: TMouseButton; Shift: TShiftState;
  3569.   X, Y: Integer);
  3570. var
  3571.   Cell: TIvGridCoord;
  3572.   SaveState: TIvGridState;
  3573. begin
  3574.   SaveState := FGridState;
  3575.   inherited MouseUp(Button, Shift, X, Y);
  3576.   if (SaveState = gsRowSizing) or (SaveState = gsColSizing) or
  3577.     ((InplaceEditor <> nil) and (InplaceEditor.Visible) and
  3578.      (PtInRect(InplaceEditor.BoundsRect, Point(X,Y)))) then Exit;
  3579.   Cell := MouseCoord(X,Y);
  3580.   if (Button = mbLeft) and (Cell.X >= FIndicatorOffset) and (Cell.Y >= 0) then
  3581.     if Cell.Y < FTitleOffset then
  3582.       TitleClick(Columns[RawToDataColumn(Cell.X)])
  3583.     else
  3584.       CellClick(Columns[SelectedIndex]);
  3585. end;
  3586.  
  3587. procedure TIvCustomDBGrid.MoveCol(RawCol: Integer);
  3588. var
  3589.   OldCol: Integer;
  3590. begin
  3591.   FDatalink.UpdateData;
  3592.   if RawCol >= ColCount then
  3593.     RawCol := ColCount - 1;
  3594.   if RawCol < FIndicatorOffset then RawCol := FIndicatorOffset;
  3595.   OldCol := Col;
  3596.   if RawCol <> OldCol then
  3597.   begin
  3598.     if not FInColExit then
  3599.     begin
  3600.       FInColExit := True;
  3601.       try
  3602.         ColExit;
  3603.       finally
  3604.         FInColExit := False;
  3605.       end;
  3606.       if Col <> OldCol then Exit;
  3607.     end;
  3608.     if not (dgAlwaysShowEditor in Options) then HideEditor;
  3609.     Col := RawCol;
  3610.     ColEnter;
  3611.   end;
  3612. end;
  3613.  
  3614. procedure TIvCustomDBGrid.Notification(AComponent: TComponent;
  3615.   Operation: TOperation);
  3616. var
  3617.   I: Integer;
  3618.   NeedLayout: Boolean;
  3619. begin
  3620.   inherited Notification(AComponent, Operation);
  3621.   if (Operation = opRemove) then
  3622.   begin
  3623.     if (AComponent is TPopupMenu) then
  3624.     begin
  3625.       for I := 0 to Columns.Count-1 do
  3626.         if Columns[I].PopupMenu = AComponent then
  3627.           Columns[I].PopupMenu := nil;
  3628.     end
  3629.     else if (FDataLink <> nil) then
  3630.       if (AComponent = DataSource)  then
  3631.         DataSource := nil
  3632.       else if (AComponent is TField) then
  3633.       begin
  3634.         NeedLayout := False;
  3635.         BeginLayout;
  3636.         try
  3637.           for I := 0 to Columns.Count-1 do
  3638.             with Columns[I] do
  3639.               if Field = AComponent then
  3640.               begin
  3641.                 Field := nil;
  3642.                 NeedLayout := True;
  3643.               end;
  3644.         finally
  3645.           if NeedLayout and Assigned(FDatalink.Dataset)
  3646.             and not FDatalink.Dataset.ControlsDisabled then
  3647.             EndLayout
  3648.           else
  3649.             DeferLayout;
  3650.         end;
  3651.       end;
  3652.   end;
  3653. end;
  3654.  
  3655. procedure TIvCustomDBGrid.RecordChanged(Field: TField);
  3656. var
  3657.   I: Integer;
  3658.   CField: TField;
  3659. begin
  3660.   if not HandleAllocated then Exit;
  3661.   if Field = nil then
  3662.     Invalidate
  3663.   else
  3664.   begin
  3665.     for I := 0 to Columns.Count - 1 do
  3666.       if Columns[I].Field = Field then
  3667.         InvalidateCol(DataToRawColumn(I));
  3668.   end;
  3669.   CField := SelectedField;
  3670.   if ((Field = nil) or (CField = Field)) and
  3671.     (Assigned(CField) and (CField.Text <> FEditText)) then
  3672.   begin
  3673.     InvalidateEditor;
  3674.     if InplaceEditor <> nil then InplaceEditor.Deselect;
  3675.   end;
  3676. end;
  3677.  
  3678. procedure TIvCustomDBGrid.Scroll(Distance: Integer);
  3679. var
  3680.   OldRect, NewRect: TRect;
  3681.   RowHeight: Integer;
  3682. begin
  3683.   if not HandleAllocated then Exit;
  3684.   OldRect := BoxRect(0, Row, ColCount - 1, Row);
  3685.   if (FDataLink.ActiveRecord >= RowCount - FTitleOffset) then UpdateRowCount;
  3686.   UpdateScrollBar;
  3687.   UpdateActive;
  3688.   NewRect := BoxRect(0, Row, ColCount - 1, Row);
  3689.   ValidateRect(Handle, @OldRect);
  3690.   InvalidateRect(Handle, @OldRect, False);
  3691.   InvalidateRect(Handle, @NewRect, False);
  3692.   if Distance <> 0 then
  3693.   begin
  3694.     HideEditor;
  3695.     try
  3696.       if Abs(Distance) > VisibleRowCount then
  3697.       begin
  3698.         Invalidate;
  3699.         Exit;
  3700.       end
  3701.       else
  3702.       begin
  3703.         RowHeight := DefaultRowHeight;
  3704.         if dgRowLines in Options then Inc(RowHeight, GridLineWidth);
  3705.         if dgIndicator in Options then
  3706.         begin
  3707.           OldRect := BoxRect(0, FSelRow, ColCount - 1, FSelRow);
  3708.           InvalidateRect(Handle, @OldRect, False);
  3709.         end;
  3710.         NewRect := BoxRect(0, FTitleOffset, ColCount - 1, 1000);
  3711.         ScrollWindowEx(Handle, 0, -RowHeight*Distance, @NewRect, @NewRect,
  3712.           0, nil, SW_Invalidate);
  3713.         if dgIndicator in Options then
  3714.         begin
  3715.           NewRect := BoxRect(0, Row, ColCount - 1, Row);
  3716.           InvalidateRect(Handle, @NewRect, False);
  3717.         end;
  3718.       end;
  3719.     finally
  3720.       if dgAlwaysShowEditor in Options then ShowEditor;
  3721.     end;
  3722.   end;
  3723.   if UpdateLock = 0 then Update;
  3724. end;
  3725.  
  3726. procedure TIvCustomDBGrid.SeTIvColumns(Value: TIvDBGridColumns);
  3727. begin
  3728.   Columns.Assign(Value);
  3729. end;
  3730.  
  3731. function ReadOnlyField(Field: TField): Boolean;
  3732. var
  3733.   MasterField: TField;
  3734. begin
  3735.   Result := Field.ReadOnly;
  3736.   if not Result and (Field.FieldKind = fkLookup) then
  3737.   begin
  3738.     Result := True;
  3739.     if Field.DataSet = nil then Exit;
  3740.     MasterField := Field.Dataset.FindField(Field.KeyFields);
  3741.     if MasterField = nil then Exit;
  3742.     Result := MasterField.ReadOnly;
  3743.   end;
  3744. end;
  3745.  
  3746. procedure TIvCustomDBGrid.SeTIvColumnAttributes;
  3747. var
  3748.   I: Integer;
  3749. begin
  3750.   for I := 0 to FColumns.Count-1 do
  3751.   with FColumns[I] do
  3752.   begin
  3753.     TabStops[I + FIndicatorOffset] := not ReadOnly and DataLink.Active and
  3754.       Assigned(Field) and not (Field.FieldKind = fkCalculated) and not ReadOnlyField(Field);
  3755.     ColWidths[I + FIndicatorOffset] := Width;
  3756.   end;
  3757.   if (dgIndicator in Options) then
  3758.     ColWidths[0] := IndicatorWidth;
  3759. end;
  3760.  
  3761. procedure TIvCustomDBGrid.SetDataSource(Value: TDataSource);
  3762. begin
  3763.   if Value = FDatalink.Datasource then Exit;
  3764.   FBookmarks.Clear;
  3765.   FDataLink.DataSource := Value;
  3766.   if Value <> nil then Value.FreeNotification(Self);
  3767.   LinkActive(FDataLink.Active);
  3768. end;
  3769.  
  3770. procedure TIvCustomDBGrid.SetEditText(ACol, ARow: Longint; const Value: string);
  3771. begin
  3772.   FEditText := Value;
  3773. end;
  3774.  
  3775. procedure TIvCustomDBGrid.SetOptions(Value: TIvDBGridOptions);
  3776. const
  3777.   LayoutOptions = [dgEditing, dgAlwaysShowEditor, dgTitles, dgIndicator,
  3778.     dgColLines, dgRowLines, dgRowSelect, dgAlwaysShowSelection];
  3779. var
  3780.   NewGridOptions: TIvGridOptions;
  3781.   ChangedOptions: TIvDBGridOptions;
  3782. begin
  3783.   if FOptions <> Value then
  3784.   begin
  3785.     NewGridOptions := [];
  3786.     if dgColLines in Value then
  3787.       NewGridOptions := NewGridOptions + [goFixedVertLine, goVertLine];
  3788.     if dgRowLines in Value then
  3789.       NewGridOptions := NewGridOptions + [goFixedHorzLine, goHorzLine];
  3790.     if dgColumnResize in Value then
  3791.       NewGridOptions := NewGridOptions + [goColSizing, goColMoving];
  3792.     if dgTabs in Value then Include(NewGridOptions, goTabs);
  3793.     if dgRowSelect in Value then
  3794.     begin
  3795.       Include(NewGridOptions, goRowSelect);
  3796.       Exclude(Value, dgAlwaysShowEditor);
  3797.       Exclude(Value, dgEditing);
  3798.     end;
  3799.     if dgEditing in Value then Include(NewGridOptions, goEditing);
  3800.     if dgAlwaysShowEditor in Value then Include(NewGridOptions, goAlwaysShowEditor);
  3801.     inherited Options := NewGridOptions;
  3802.     if dgMultiSelect in (FOptions - Value) then FBookmarks.Clear;
  3803.     ChangedOptions := (FOptions + Value) - (FOptions * Value);
  3804.     FOptions := Value;
  3805.     if ChangedOptions * LayoutOptions <> [] then LayoutChanged;
  3806.   end;
  3807. end;
  3808.  
  3809. procedure TIvCustomDBGrid.SetSelectedField(Value: TField);
  3810. var
  3811.   I: Integer;
  3812. begin
  3813.   if Value = nil then Exit;
  3814.   for I := 0 to Columns.Count - 1 do
  3815.     if Columns[I].Field = Value then
  3816.       MoveCol(DataToRawColumn(I));
  3817. end;
  3818.  
  3819. procedure TIvCustomDBGrid.SetSelectedIndex(Value: Integer);
  3820. begin
  3821.   MoveCol(DataToRawColumn(Value));
  3822. end;
  3823.  
  3824. procedure TIvCustomDBGrid.SetTitleFont(Value: TFont);
  3825. begin
  3826.   FTitleFont.Assign(Value);
  3827.   if dgTitles in Options then LayoutChanged;
  3828. end;
  3829.  
  3830. function TIvCustomDBGrid.StoreColumns: Boolean;
  3831. begin
  3832.   Result := Columns.State = csCustomized;
  3833. end;
  3834.  
  3835. procedure TIvCustomDBGrid.TimedScroll(Direction: TIvGridScrollDirection);
  3836. begin
  3837.   if FDatalink.Active then
  3838.   begin
  3839.     with FDatalink do
  3840.     begin
  3841.       if sdUp in Direction then
  3842.       begin
  3843.         DataSet.MoveBy(-ActiveRecord - 1);
  3844.         Exclude(Direction, sdUp);
  3845.       end;
  3846.       if sdDown in Direction then
  3847.       begin
  3848.         DataSet.MoveBy(RecordCount - ActiveRecord);
  3849.         Exclude(Direction, sdDown);
  3850.       end;
  3851.     end;
  3852.     if Direction <> [] then inherited TimedScroll(Direction);
  3853.   end;
  3854. end;
  3855.  
  3856. procedure TIvCustomDBGrid.TitleClick(Column: TIvColumn);
  3857. begin
  3858.   if Assigned(FOnTitleClick) then FOnTitleClick(Column);
  3859. end;
  3860.  
  3861. procedure TIvCustomDBGrid.TitleFontChanged(Sender: TObject);
  3862. begin
  3863.   if (not FSelfChangingTitleFont) and not (csLoading in ComponentState) then
  3864.     ParentFont := False;
  3865.   if dgTitles in Options then LayoutChanged;
  3866. end;
  3867.  
  3868. procedure TIvCustomDBGrid.UpdateActive;
  3869. var
  3870.   NewRow: Integer;
  3871.   Field: TField;
  3872. begin
  3873.   if FDatalink.Active and HandleAllocated and not (csLoading in ComponentState) then
  3874.   begin
  3875.     NewRow := FDatalink.ActiveRecord + FTitleOffset;
  3876.     if Row <> NewRow then
  3877.     begin
  3878.       if not (dgAlwaysShowEditor in Options) then HideEditor;
  3879.       MoveColRow(Col, NewRow, False, False);
  3880.       InvalidateEditor;
  3881.     end;
  3882.     Field := SelectedField;
  3883.     if Assigned(Field) and (Field.Text <> FEditText) then
  3884.       InvalidateEditor;
  3885.   end;
  3886. end;
  3887.  
  3888. procedure TIvCustomDBGrid.UpdateData;
  3889. var
  3890.   Field: TField;
  3891. begin
  3892.   Field := SelectedField;
  3893.   if Assigned(Field) then
  3894.     Field.Text := FEditText;
  3895. end;
  3896.  
  3897. procedure TIvCustomDBGrid.UpdateRowCount;
  3898. begin
  3899.   if RowCount <= FTitleOffset then RowCount := FTitleOffset + 1;
  3900.   FixedRows := FTitleOffset;
  3901.   with FDataLink do
  3902.     if not Active or (RecordCount = 0) or not HandleAllocated then
  3903.       RowCount := 1 + FTitleOffset
  3904.     else
  3905.     begin
  3906.       RowCount := 1000;
  3907.       FDataLink.BufferCount := VisibleRowCount;
  3908.       RowCount := RecordCount + FTitleOffset;
  3909.       if dgRowSelect in Options then TopRow := FixedRows;
  3910.       UpdateActive;
  3911.     end;
  3912. end;
  3913.  
  3914. procedure TIvCustomDBGrid.UpdateScrollBar;
  3915. var
  3916. {$IFDEF IVWIDE}
  3917.   SIOld, SINew: TScrollInfo;
  3918. {$ELSE}
  3919.   Pos: Integer;
  3920. {$ENDIF}
  3921. begin
  3922.   if FDatalink.Active and HandleAllocated then
  3923.     with FDatalink.DataSet do
  3924.     begin
  3925. {$IFDEF IVWIDE}
  3926.       SIOld.cbSize := sizeof(SIOld);
  3927.       SIOld.fMask := SIF_ALL;
  3928.       GetScrollInfo(Self.Handle, SB_VERT, SIOld);
  3929.       SINew := SIOld;
  3930.       if IsSequenced then
  3931.       begin
  3932.         SINew.nMin := 1;
  3933.         SINew.nPage := Self.VisibleRowCount;
  3934.         SINew.nMax := RecordCount + SINew.nPage -1;
  3935.         if State in [dsInactive, dsBrowse, dsEdit] then
  3936.           SINew.nPos := RecNo;  // else keep old pos
  3937.       end
  3938.       else
  3939.       begin
  3940.         SINew.nMin := 0;
  3941.         SINew.nPage := 0;
  3942.         SINew.nMax := 4;
  3943.         if BOF then SINew.nPos := 0
  3944.         else if EOF then SINew.nPos := 4
  3945.         else SINew.nPos := 2;
  3946.       end;
  3947.       if (SINew.nMin <> SIOld.nMin) or (SINew.nMax <> SIOld.nMax) or
  3948.         (SINew.nPage <> SIOld.nPage) or (SINew.nPos <> SIOld.nPos) then
  3949.         SetScrollInfo(Self.Handle, SB_VERT, SINew, True);
  3950. {$ELSE}
  3951.       SetScrollRange(Self.Handle, SB_VERT, 0, 4, False);
  3952.       if BOF then
  3953.         Pos := 0
  3954.       else if EOF then
  3955.         Pos := 4
  3956.       else
  3957.         Pos := 2;
  3958.       if GetScrollPos(Self.Handle, SB_VERT) <> Pos then
  3959.         SetScrollPos(Self.Handle, SB_VERT, Pos, True);
  3960. {$ENDIF}
  3961.     end;
  3962. end;
  3963.  
  3964. function TIvCustomDBGrid.ValidFieldIndex(FieldIndex: Integer): Boolean;
  3965. begin
  3966.   Result := DataLink.GetMappedIndex(FieldIndex) >= 0;
  3967. end;
  3968.  
  3969. procedure TIvCustomDBGrid.CMParentFontChanged(var Message: TMessage);
  3970. begin
  3971.   inherited;
  3972.   if ParentFont then
  3973.   begin
  3974.     FSelfChangingTitleFont := True;
  3975.     try
  3976.       TitleFont := Font;
  3977.     finally
  3978.       FSelfChangingTitleFont := False;
  3979.     end;
  3980.     LayoutChanged;
  3981.   end;
  3982. end;
  3983.  
  3984. procedure TIvCustomDBGrid.CMExit(var Message: TMessage);
  3985. begin
  3986.   try
  3987.     if FDatalink.Active then
  3988.       with FDatalink.Dataset do
  3989.         if (dgCancelOnExit in Options) and (State = dsInsert) and
  3990.           not Modified and not FDatalink.FModified then
  3991.           Cancel else
  3992.           FDataLink.UpdateData;
  3993.   except
  3994.     SetFocus;
  3995.     raise;
  3996.   end;
  3997.   inherited;
  3998. end;
  3999.  
  4000. procedure TIvCustomDBGrid.CMFontChanged(var Message: TMessage);
  4001. var
  4002.   I: Integer;
  4003. begin
  4004.   inherited;
  4005.   BeginLayout;
  4006.   try
  4007.     for I := 0 to Columns.Count-1 do
  4008.       Columns[I].RefreshDefaultFont;
  4009.   finally
  4010.     EndLayout;
  4011.   end;
  4012. end;
  4013.  
  4014. procedure TIvCustomDBGrid.CMDeferLayout(var Message);
  4015. begin
  4016.   if AcquireLayoutLock then
  4017.     EndLayout
  4018.   else
  4019.     DeferLayout;
  4020. end;
  4021.  
  4022. procedure TIvCustomDBGrid.CMDesignHitTest(var Msg: TCMDesignHitTest);
  4023. begin
  4024.   inherited;
  4025.   if (Msg.Result = 1) and ((FDataLink = nil) or
  4026.     ((Columns.State = csDefault) and
  4027.      (FDataLink.DefaultFields or (not FDataLink.Active)))) then
  4028.     Msg.Result := 0;
  4029. end;
  4030.  
  4031. procedure TIvCustomDBGrid.WMSetCursor(var Msg: TWMSetCursor);
  4032. begin
  4033.   if (csDesigning in ComponentState) and ((FDataLink = nil) or
  4034.      ((Columns.State = csDefault) and
  4035.       (FDataLink.DefaultFields or (not FDataLink.Active)))) then
  4036.     Windows.SetCursor(LoadCursor(0, IDC_ARROW))
  4037.   else inherited;
  4038. end;
  4039.  
  4040. procedure TIvCustomDBGrid.WMSize(var Message: TWMSize);
  4041. begin
  4042.   inherited;
  4043.   if UpdateLock = 0 then UpdateRowCount;
  4044. end;
  4045.  
  4046. procedure TIvCustomDBGrid.WMVScroll(var Message: TWMVScroll);
  4047. {$IFDEF IVWIDE}
  4048. var
  4049.   SI: TScrollInfo;
  4050. {$ENDIF}
  4051. begin
  4052.   if not AcquireFocus then Exit;
  4053.   if FDatalink.Active then
  4054.     with Message, FDataLink.DataSet do
  4055.       case ScrollCode of
  4056.         SB_LINEUP: MoveBy(-FDatalink.ActiveRecord - 1);
  4057.         SB_LINEDOWN: MoveBy(FDatalink.RecordCount - FDatalink.ActiveRecord);
  4058.         SB_PAGEUP: MoveBy(-VisibleRowCount);
  4059.         SB_PAGEDOWN: MoveBy(VisibleRowCount);
  4060.         SB_THUMBPOSITION:
  4061.           begin
  4062. {$IFDEF IVWIDE}
  4063.             if IsSequenced then
  4064.             begin
  4065.               SI.cbSize := sizeof(SI);
  4066.               SI.fMask := SIF_ALL;
  4067.               GetScrollInfo(Self.Handle, SB_VERT, SI);
  4068.               if SI.nTrackPos <= 1 then First
  4069.               else if SI.nTrackPos >= RecordCount then Last
  4070.               else RecNo := SI.nTrackPos;
  4071.             end
  4072.             else
  4073. {$ENDIF}
  4074.               case Pos of
  4075.                 0: First;
  4076.                 1: MoveBy(-VisibleRowCount);
  4077.                 2: Exit;
  4078.                 3: MoveBy(VisibleRowCount);
  4079.                 4: Last;
  4080.               end;
  4081.           end;
  4082.         SB_BOTTOM: Last;
  4083.         SB_TOP: First;
  4084.       end;
  4085. end;
  4086.  
  4087. {$IFDEF IVIME}
  4088. procedure TIvCustomDBGrid.SetIme;
  4089. var
  4090.   Column: TIvColumn;
  4091. begin
  4092.   if not SysLocale.Fareast then
  4093.     Exit;
  4094.  
  4095.   if FUpdatingEditor or FDataLink.FInUpdateData then
  4096.   begin
  4097.     ImeName := Screen.DefaultIme;
  4098.     ImeMode := imDontCare;
  4099.   end
  4100.   else
  4101.   begin
  4102.     Column := Columns[SelectedIndex];
  4103.     ImeName := FOriginalImeName;
  4104.     ImeMode := FOriginalImeMode;
  4105.     if cvImeMode in Column.FAssignedValues then
  4106.     begin
  4107.       ImeName := Column.ImeName;
  4108.       ImeMode := Column.ImeMode;
  4109.     end;
  4110.   end;
  4111.  
  4112.   if InplaceEditor <> nil then
  4113.   begin
  4114.     TIvDBGridInplaceEdit(Self).ImeName := ImeName;
  4115.     TIvDBGridInplaceEdit(Self).ImeMode := ImeMode;
  4116.   end;
  4117. end;
  4118.  
  4119. procedure TIvCustomDBGrid.UpdateIme;
  4120. begin
  4121.   if not SysLocale.Fareast then Exit;
  4122.   SetIme;
  4123.   if InplaceEditor <> nil then
  4124.     TIvDBGridInplaceEdit(Self).SetIme;
  4125. end;
  4126.  
  4127. procedure TIvCustomDBGrid.WMIMEStartComp(var Message: TMessage);
  4128. begin
  4129.   inherited;
  4130.   FUpdatingEditor := True;
  4131.   ShowEditor;
  4132.   FUpdatingEditor := False;
  4133. end;
  4134.  
  4135. procedure TIvCustomDBGrid.WMSetFocus(var Message: TWMSetFocus);
  4136. begin
  4137.   SetIme;
  4138.   inherited;
  4139. end;
  4140.  
  4141. procedure TIvCustomDBGrid.WMKillFocus(var Message: TMessage);
  4142. begin
  4143.   ImeName := Screen.DefaultIme;
  4144.   ImeMode := imDontCare;
  4145.   inherited;
  4146. end;
  4147. {$ENDIF}
  4148.  
  4149. {$ENDIF}
  4150.  
  4151. end.
  4152.